home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-03-20 | 181.3 KB | 3,312 lines |
- -----------------------------------------------------------------------------
- LangWin Version 2.3 (C) Copyright Allen L. Lang, 1993 ALL RIGHTS RESERVED
- -----------------------------------------------------------------------------
-
- LangWin is a QuickBASIC program development toolkit. It contains a library of
- functions and subroutines that can be used to easily create standard Graphical
- User Interface (GUI) features in your compiled text-mode QuickBASIC or BASIC
- PDS programs (sorry, LangWin cannot be used with the QBASIC interpreter in DOS
- 5.0).
-
- GUI features supported include: opening/closing windows with drop-shadows,
- scrollable text, push buttons, click boxes, dialog boxes, editable input
- fields, etc. LangWin's GUI routines support both mouse and keyboard input.
- Windows created by LangWin can be moved and resized (via mouse only). LangWin
- also contains stand-alone routines that can be used to add mouse support to
- your QuickBASIC programs that do not use LangWin. Mouse features include:
- initialization, get/set position, get button status, hide/show cursor, set
- horizontal/vertical limits, etc. Finally, LangWin has several functions that
- allow you to get the default drive and directory, change the default
- drive/directory, and extract all file/sub-directory names from the current
- directory. All features are documented below.
-
-
- Let's get the formalities out of the way before exploring LangWin's
- capabilities ...
-
-
-
- LICENSE
- -------
- All rights are reserved. LangWin is licensed free of charge. LangWin may be
- copied and distributed provided: that all files are copied from this package,
- as is, and in their entirety, and that no fee is charged for such copies beyond
- the expense of duplication, media, and distribution. The author's rights extend
- to all copies so made. Permission is granted to distribute all programs
- developed using LangWin.
-
-
- WARRANTY
- --------
- LangWin is not covered by any warranty, expressed or implied. The author will
- not be liable for anything that happens as a result of any use of, or the
- inability to use, LangWin in your particular application. Stated another way:
-
-
- BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
- WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
- LAW. THIS PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
- KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED
- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
- PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM
- PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
- REPAIR OR CORRECTION.
-
-
-
-
- ==========================================================================
-
-
- Phew, I hate that part. Now let's get to the good stuff: what is LangWin
- and how can you use it to create some great looking BASIC programs ...
-
-
- --------------------
- LANGWIN USER'S GUIDE
- --------------------
-
- --------
- CONTENTS
- --------
-
- 0.0 CHRONOLOGY OF CHANGES IN LANGWIN'S VERSIONS
- 0.1 Notes for Those Upgrading From Previous Versions of LangWin
- 1.0 REQUIREMENTS AND RESTRICTIONS
- 2.0 GETTING STARTED
- 3.0 DOING "BASIC" WINDOWS
- 3.1 Initialization
- 3.2 Some Simple Windows
- 4.0 USING LANGWIN'S GUI
- 4.1 Close Icon
- 4.2 Moving, Resizing, and Minimizing the Active Window
- 4.3 Focus
- 4.4 Changing Focus (window & object focus)
- 4.5 Scrolling Text
- 4.6 Selecting Scrollable Text
- 4.7 Push Buttons
- 4.8 Check Boxes
- 4.9 Input Fields
- 4.10 No mouse
- 5.0 DATA STRUCTURES AND GLOBAL VARIABLES
- 5.1 MaxWindows
- 5.2 MaxButtons
- 5.3 MaxTextLines
- 5.4 MaxTextWins
- 5.5 AnyWinOpen
- 5.6 CurWinPtr
- 5.7 Window numbers vs window handles
- 5.8 SaveText
- 5.9 WinParms
- 5.10 button handles
- 5.11 ButtonsText
- 5.12 ButtonsData
- 5.13 UserHotKeys
- 5.14 WinNum
- 6.0 ADVANCED WINDOWS
- 6.1 Adding Frills to Your Windows
- 6.2 Window Modes, Shadows, and Movement
- 6.3 Using Wallpaper Windows (Info Only Window and
- Multiple Lists In One Window)
- 6.4 Modifying Scrollable Text While Its Window Is Open
- 6.5 Changing The Entire Scrollable Text Array While Its Window Is Open
- 6.6 Modifying the Contents of an Input Field While Its Window Is Open
- 6.7 Determining What Had Focus When WinEvent Returns Control
- 6.8 Nesting Calls to WinEvent
- 6.9 Deactivating and Activating Buttons
- 6.10 Giving a Specific Button Focus When It Is Created
- 6.11 Is A Given Window Open
- 6.12 Manually Giving a Window Focus (i.e., via program control)
- 6.13 State of Check Boxes
- 6.14 Run Time Errors
- 6.15 Testing for Color, B/W, EGA, or VGA
- 6.16 Colors, Attributes, and Palettes
- 6.17 WaitTicks
- 6.18 Video Pages
- 6.19 Using Non-Text Mode Graphics With LangWin
- 6.20 Closing Windows
- 6.21 Modifying Static Text Created With ShowWinText
- 6.22 "Time Out" Feature of WinEvent for Interrupt Buttons
- 6.23 Dynamically Adding Entries to a Visible List of Scrollable Text
- 7.0 MOUSE ROUTINES
- 8.0 DIRECTORY AND FILE ROUTINES
- 9.0 COMING ATTRACTIONS
- 10.0 CONTACTING THE AUTHOR
-
-
-
- -----------------------------------------------
- 0.0 CHRONOLOGY OF CHANGES IN LANGWIN'S VERSIONS
- -----------------------------------------------
- You're right! I added this section after the user's guide was complete, and I
- didn't feel like re-numbering all following sections (I'm using a plain vanilla
- text editor). So, this section gets number zero. I guess that means the next
- section that needs to be inserted must have a negative number!
-
- Major changes implemented in LangWin's Versions:
-
- 1.0 Never released. Finally got LangWin packaged, but I thought of too many
- additional features that I wanted to include. [2/92]
-
- 1.1 First version of LangWin released. [4/28/92]
-
- 1.2 - Simplified the license statement.
- - Updated Section 6.5 of user's guide.
- - Significant performance improvement in window open/close by
- writing/reading directly to/from video buffer (DEF SEG=&HB800).
- (so don't relocate/reuse the upper memory block at segment &HB800).
- [5/23/92]
-
-
- 2.0 Major re-write. Removed the restriction that "locked" the mouse onto
- the current window. With 2.0, you can open multiple windows, mouse
- to any visible window, click on any button, scroll any text, etc.
- (rather than being restricted to just the current window in focus).
- I've also added several new window features such as shadowless,
- unmovable, modal (you must close it before any other window can be
- selected), immediate close (clicking on another window will automatically
- close it), and wallpaper (background only). These new features require
- a slightly different programming technique than LangWin 1.x, so most
- code developed for LangWin 1.x will need to be updated (new calling
- parameters for some routines and slightly new logic in the WinEvent
- loop). Sorry, but after you start using 2.0, I think you'll agree
- that the hassle of re-coding was worth the new features provided.
- The new features are described in this User's Guide.
-
- Both QuickBASIC and BASIC PDS are now supported.
-
- I've also moved the online documentation for all LangWin's routines
- from a stand-alone program (which required you to exit QB and run the
- program to get info on a particular routine), to a module (WINHELP.BAS)
- that can be loaded into QB with your program under development. Thus,
- you need only hit the F2 key while developing with LangWin in the QB
- environment to get online help for any routine.
- [10/25/92]
-
- 2.1 Window resizing and minimizing added.
-
- ChangeButtonFocus routine added to allow a specific object to be given
- focus when a window is first opened (i.e., creating a default button).
- Section 6.10 has more details and examples.
-
- Some parameters in LangWin's data structures were changed
- (section 5.0 has most current description).
-
- ActivateButton routine no longer needs a string variable with the
- Buttons's text. The Button's original text is remembered when it is
- deactivated (DeactivateButton) and automatically restored.
- If converting from V2.0, remove the string parameter from calls
- to ActivateButton or you'll get a parameter mismatch error.
- [11/21/92]
-
- 2.2 Fixed misc bugs.
- - Buttons at top of screen could not be moved or resized.
- - When minimizing and restoring window to original size,
- window would be 2 columns larger than original.
- - Code in WinEvent required left mouse button to be down
- at the actual time the mouse was polled (in order to "recognize" a
- click on the left button). On slow machines, it was possible
- for the left button to have been clicked and released at the point in
- time when the mouse was polled. Symptom was that left mouse button
- was ignored. Code now only tests to determine if left mouse
- button had been pressed any time since the last time mouse was polled
- (current state of the left button at the time the mouse is polled
- is no longer tested). The state of the right button is still
- tested at the time the mouse is polled (to distinguish between
- a left click, a right click, and both buttons clicked).
-
- The mouse remains active when an input field is selected for data entry.
- - Mouse can be used to place text cursor anywhere in the input field.
- - Mouse can be used to exit input mode by clicking anywhere outside
- the input field (including clicking on wallpaper, another window,
- another button, another inputfield, etc.).
-
- The input field can be used for password entry. All text entered will
- be displayed with the * character; the actual text entered will be
- saved in ButtonsText(handle) - just as any input field's text.
- Use a negative value for the input field's length (in MakeInputField) to
- cause the input field to be used in this way. An alternative
- technique, available in previous releases, would be to define the input
- field with the same values for foreground and background colors. In this
- case, nothing will be visible when text is entered.
- [12/12/92]
-
-
- 2.3 Several new hints & tips were added to Section 6.0 (Advanced Windows)
- of this User's Guide (i.e., 6.18 - 6.23); other sub-sections were updated
- (6.3.2 and 6.16). Previous users of LangWin should scan all of
- Section 6.0 and read the new sub-sections.
-
- A new option ("time out") was added to WinEvent. With the time out option
- enabled, WinEvent will automatically return control to your program after
- 0.5 sec if no events are detected. The time out option can be used to
- implement an "interrupt" button for long running tasks. See Section 6.22
- and SAMPLE05.BAS for additional details and sample code.
-
- A new function was added (GrowScrollText) that allows you to dynamically
- add entries to a visible list of scrollable text (i.e., after the
- scrollable text window has been opened). This can be used to
- give the user real-time feedback while a long running task is underway.
- For example, suppose you are searching an entire hard disk for all files
- that match a given specification. As each file name is found, it can be
- added to a visible list of scrollable text in the current window.
- Section 6.23 contains additional details and SAMPLE05.BAS illustrates
- the use of GrowScrollText.
-
- Most of the SAMPLE programs included on the distribution disk were
- cleaned up. Standard code is used in the main modules to initialize
- (see Section 3.1 for examples). SAMPLE04.BAS was re-written (it still
- performs the same task). SAMPLE05.BAS is new with V2.3.
-
- The GetFileNames function (see Section 8.0) has been modified.
- This functions places all file or directory names that match a specific
- criteria into a string array (whose name is passed as a parameter).
- Previously, this string array had to be DIMed to a given size, and
- if the array was filled, an error code was returned. With V2.3, the
- size of string array is automatically adjusted by GetFileNames to
- hold all of the names that match the given criteria. The string array,
- however, MUST be DYNAMIC or a "duplicate definition" error will occur.
- In addition, a new option was added to GetFileNames. Previously, only
- "regular" file or directory names could be extracted (i.e., not
- read only, hidden, or system). The new option allows ALL names that
- match the criteria to be extracted (both files and directories). In this
- case, the name is prefixed with a one byte string containing the value of
- the file or directory's attribute. The bits in this byte can be tested to
- determine whether the name corresponds to a file or directory, and
- whether it's regular, hidden, system, read-only, etc. Before displaying
- the name, the attribute byte must be removed, otherwise you'll get some
- strange values as the file or directory's first character. See
- WINHELP.BAS for more details on the GetFileNames function along with a
- description of the attribute byte.
-
- OpenScrollWindow has been modified. This function displays the contents
- of a string array in a scrollable window. In previous releases, if
- UBOUND of this array was greater than the MaxTextLines global variable,
- a run-time error would terminate your program when OpenScrollWindow
- was called. As of V2.3, if UBOUND of the string array > MaxTextLines,
- then only the first MaxTextLines of the array will be displayed
- (run-time error will not occur). To let your user know that this
- condition has occurred, the last line in the scrollable window will
- be set to the string: "(Incomplete List)"
-
- RefreshScrollText has been modified. This subroutine is used to
- dynamically change the scrollable text displayed in a window created via
- OpenScrollText. A string array is passed to RefreshScrollText which
- contains the new scrollable text to be placed into the current window.
- Similar to the modification made to OpenScrollWindow mentioned above,
- if UBOUND of this string array is greater than MaxTextLines, then only
- the first MaxTextLines will be displayed with the last line set to the
- string: "(Incomplete List)". A run-time error will no longer be
- generated if UBOUND of the string array is greater than MaxTextLines.
-
- When moving a window, you can no longer attempt to place it off of the
- screen (which used to cause an error message to be displayed). When
- moving a window (by dragging upper left corner with left mouse button),
- the mouse cursor will be limited (can't be moved off of the screen).
- If the window has a shadow, the limit set for the mouse cursor will
- have two columns on the right and one row at bottom reserved for
- the area where a shadow will be displayed.
-
- Any video page available on your system can now be used with LangWin
- (previous versions of LangWin only supported page 0); however, the
- SAME video page MUST be used for all windows/objects created with
- LangWin. See Section 3.1 for a discussion on initializing the video
- page with the SCREEN command, and see Section 6.18 for a discussion
- on using video pages. With V2.3, you MUST place the SCREEN command
- BEFORE the call to LangWinInit (Section 3.1 has more details).
-
- In some cases, moving small (e.g., minimized) windows could result in the
- lower right "move" cursor not being erased. This bug was corrected (it
- was never reported, I found it when testing the new code to support
- video pages beyond page 0). The bug was caused by not hiding the mouse
- cursor before doing i/o to the screen (i.e., you can't change the
- character "under" the mouse cursor without first hiding the cursor).
- After finding the above bug, I looked for other similar problems caused
- the same omission (i.e., not hiding the mouse cursor prior to writing to
- the screen). RefreshScrollText had a similar bug. If the mouse cursor
- happened to be at the very top of the scroll slider area, then the
- contents of the screen "underneath" the mouse cursor would not be updated
- correctly when the text was refreshed. This bug was also corrected.
-
- The DeactivateButton routine (introduced in V2.0) can be used to make a
- button "inactive". The button will still appear in the window, but its
- text will be cleared, and it cannot be selected (until reactivated with
- the ActivateButton routine). If a window with an "inactive" button was
- resized by moving the right side of the window to the left, the inactive
- button's length was incorrectly computed, and in some cases the inactive
- button could end up being displayed beyond the right boundary of the
- resized (smaller) window. This bug was fixed. When a window is made
- smaller, all objects will be compressed, and will eventually "disappear"
- if the window is made small enough (they will reappear when the window is
- enlarged).
-
- The Page-Down key can be used to display the next page of a scrollable
- list of text. However, if the actual scrollable list had fewer rows than
- the area allocated for scrollable text when the window was opened, and
- the Page-Down key was used, the text was not displayed correctly. The
- last physical line of the text area, rather than the last line of actual
- text, was highlighted. This highlight then remained on the screen after
- a subsequent Page-Up. This bug has been fixed.
-
- Successive calls to ActivateButton would actually toggle between the
- active/deactive state. Similarly for successive calls to
- DeactivateButton. These bugs have been fixed. Now, successive calls to
- ActivateButton (DeactivateButton) will all result in the button
- remaining active (inactive).
-
- [3/20/93]
-
- 0.1 Notes for Those Upgrading From Previous LangWin Versions
- ------------------------------------------------------------
- This section contains some information that is repeated from the previous
- section. For those who read the documentation from "cover to cover", please
- excuse the repetition. For others who skip around, I've included some of the
- key changes (as described in the previous section):
-
- For LangWin 1.x users: one change you'll have to deal with is that several
- routines now have additional calling parameters, failure codes are negative
- (i.e., in cases of failure, the value returned is the same as in V1.x, but the
- number is negative rather than positive), and some new failures are detected.
- Load the online help facility (WINHELP.BAS) into QB and scan the documentation
- for each routine. I've tried to include a "V2.0 Changes" section to help you
- spot them. Routines that should be examined for changes include: BlankWin,
- OpenScrollWindow, WinEvent, ReShowText, and ReShowPage. New routines include:
- NewFocusWindow, RefreshScrollText, IsWinOpen, ActivateButton, DeactivateButton,
- ChangeDir, ChangeDrive, GetCurDir$, GetCurDrive$, and GetFileNames. Be sure to
- scan ALL routines in WINHELP.BAS to review new features, functions, parameters,
- return codes.
-
- Another change for V1.x users is that it requires a slightly different (easier)
- programming technique. With 1.x, you needed a separate call to WinEvent for
- each window opened. Starting with 2.0, you can get by with one call to WinEvent
- for your entire program! Although, for complex programs with many menus and
- nested windows, you might want to have a separate subroutine for each menu,
- with one call to WinEvent in each subroutine. Be careful not to nest calls to
- WinEvent too deep or you'll run out of BASIC's stack space.
-
- Starting with v2.0, BlankWin and OpenScrollWindow now return a unique NUMBER
- associated with the window created (in V1.x, these routines returned a handle,
- see Section 5.7 for a discussion of the difference between these two concepts).
- This number will be used to determine which window had focus when an event
- occurred. As in V1.x, save the value returned by these routines in a unique
- variable for each window.
-
- WinEvent handles all scrolling, mouse, and keyboard events. Starting with V2.0,
- WinEvent will detect when you mouse to a different window and click on it. In
- this case, the new window will be given "focus" (by making visible any hidden
- parts of the new window that are "under" other overlapping windows). When you
- click on an object (button, scrollable text, close icon), WinEvent makes the
- corresponding window current (gives it focus). In addition, the WinEvent
- function returns a value equivalent to the number of the window with focus
- (this is the number assigned to the window when it was created by BlankWin or
- OpenScrollWindow). WinEvent takes one output parameter which is set to an code
- that defines the type of action that was selected in the window (1=close;
- 2=scrollable text selected; 3=button selected). The action code parameter is
- given the same value that the WinEvent function itself returned in LangWin 1.x.
- Between the window number and action code returned, you can determine what
- event took place and where it happened.
-
- With LangWin 2.0, several new window modes are supported. See Section 6.2 for
- details.
-
- With LangWin 2.0, both QuickBASIC and BASIC PDS are supported.
-
- With V2.1, you can now resize and minimize windows (see section 4.2). In order
- to implement this function, non-scrollable text (created with ShowWinText) has
- to be saved in LangWin's data structures (so it can be re-displayed). The
- MaxButtons global variable (see sections 3.1 and 5.2) must now be large enough
- to not only count all buttons, but also all lines of non-scrollable text. If
- you are converting from a previous version of LangWin, it's likely that you'll
- have to increase the value of MaxButtons (by the number of lines of non-
- scrollable text that could be simultaneously visible on all open windows).
- If MaxButtons is not large enough, the corresponding "make" routines that
- create objects will return an error code. New with V2.1, ShowTitle and
- ShowWinText will also return a code if there's no room in the data structures
- to save the corresponding title or static text. If you do not check for these
- error codes, the symptom you'll see is that objects you expect to see (buttons,
- static text, titles, etc.) will not be visible.
-
- With V2.1, ActivateButton routine no longer needs a string variable with the
- Buttons's text. The Button's original text is remembered when it is deactivated
- (DeactivateButton) and automatically restored when activated. If you used the
- ActivateButton routine in LangWin 2.0, just remove the string variable from the
- calling parameter list (otherwise you'll get a parameter mismatch error). If
- you used the string variable in ActivateButton to change the button's text
- prior to activating, my apologies (you can't do that anymore) but that's not
- how the parameter was intended to be used. If you need to change the button's
- text prior to activation, then change the contents of ButtonsText(han), where
- han is the button's handle. Be careful not to make the button's new text longer
- than the original button's length - which is saved in ButtonsData(han,4). See
- Section 5.11 for additional details on the button's text.
-
- With V2.3, you MUST place the SCREEN command BEFORE the call to LangWinInit.
- See Section 3.1 for additional details.
-
- With V2.3, OpenScrollWindow and RefreshScrollText no longer generate run time
- errors if UBOUND of string array (containing scrollable text) is greater than
- MaxTextLines (i.e., the maximum number of text lines DIMed in LangWin's data
- structures - see Section 3.1 for details on defining MaxTextLines). Instead,
- the number of text lines displayed will be truncated to (MaxTextLines - 1), and
- the last line displayed will appear as: "(Incomplete List)"
- Note that the contents of the string array are NOT modified, only the last line
- displayed in the scrollable text window is changed. Prior to calling
- OpenScrollWindow or RefreshScrollText, you can compare UBOUND of the string
- array to MaxTextLines to detect this condition (or just allow the text lines
- displayed to be truncated - in which case no special return code is provided).
-
- With V2.3, a new option has been added to GetFileNames. Previously,
- GetFileNames only returned "regular" file or directory names (i.e., not those
- that were read-only, hidden, or system). The new option will return ALL names
- (that match the file specification) along with an attribute byte (which is
- included as the first character of the name). You must test the bits of this
- attribute byte to determine if the name is a file or directory, and if it's
- regular, hidden, system, read-only, etc. Then, before displaying the name, you
- must remove the attribute byte (or the first character of the name will look
- very funny). See the GetFileNames member in WINHELP.BAS for more details, along
- with a description of the bits within the attribute byte.
-
- With V2.3, GetFileNames will automatically REDIM the array passed to hold all
- names that match the file specification. Previously, the array had to be pre-
- DIMed large enough to hold all names (or an error code was returned if the
- array was not large enough). Now, just pass an array that has been DIMed (1)
- and GetFileNames will re-adjust the array to the correct size. Note, the array
- MUST be DYNAMIC. LBOUND of the returned array will be 1. See WINHELP.BAS for
- more information.
-
- With V2.3, a new option ("time out") was added to WinEvent. With the time out
- option enabled, WinEvent will automatically return control to your program
- after 0.5 sec if no events are detected. The time out option can be used to
- implement an "interrupt" button for long running tasks. See Section 6.22 and
- SAMPLE05.BAS for additional details and sample code.
-
- With V2.3, a new function was added (GrowScrollText) that allows you to
- dynamically add entries to the bottom of a visible list of scrollable text
- (i.e., after the scrollable text window has been opened, you can append text).
- This can be used to give the user real-time feedback while a long running task
- is underway. For example, suppose you are searching an entire hard disk for
- all files that match a given specification. As each file name is found, it can
- be added to a visible list of scrollable text in the current window. Section
- 6.23 contains additional details and SAMPLE05.BAS illustrates the use of
- GrowScrollText.
-
-
-
-
- Within this user's guide and the online help (WINHELP.BAS), I've tried to
- include the corresponding LangWin version numbers when describing new
- features/functions. Users of previous LangWin versions can scan for appropriate
- character strings (i.e., 2.0, 2.1, etc.) to quickly find changes. However, I
- would still encourage previous users to read this ENTIRE guide and ALL members
- in WINHELP.BAS since, in many cases, entire sections have been re-written.
-
-
- ---------------------------------
- 1.0 REQUIREMENTS AND RESTRICTIONS
- ---------------------------------
- LangWin is a GUI toolkit for QuickBASIC or BASIC PDS developers. You must have
- one of these compilers to use LangWin.
-
- Programs developed with LangWin are meant to run under DOS (see Windows
- restriction later in this section). I developed LangWin using DOS 4.01 and did
- some testing with DOS 5.0. LangWin was developed entirely in QuickBASIC, so it
- should be compatible with earlier versions of DOS (but I did not do any
- explicit testing with earlier DOS versions).
-
- LangWin creates color text-mode (SCREEN 0) windows. You must have a color
- monitor with EGA graphics or better (but also see Section 6.19).
-
- A mouse is not required to take advantage of LangWin's GUI features, but
- as you probably know, using any GUI from the keyboard PAINFUL!
-
- LangWin is a quick library. It is object code, NOT source. (You CANNOT use
- LangWin with DOS 5.0's QBASIC interpreter). I used QB 4.5 to develop LangWin
- (which was written ENTIRELY in QuickBASIC to demonstrate how powerful this
- language really is). LangWin is very similar to the User Interface (Menu,
- Window, Mouse, and General) routines included with the BASIC Professional
- Development System (PDS) V7.1. Actually, I think LangWin is easier to use, but
- I might not be totally objective in my assessment!
-
- Since the release of V2.0, there have been two "flavors" of LangWin's
- libraries: one for QuickBasic (called LANGWIN.QLB and LANGWIN.LIB) and one for
- BASIC PDS (called LANGWINP.QLB and LANGWINP.LIB). As previously mentioned, I
- did all of my development and testing in QuickBASIC 4.5. I created the PDS
- libraries and ran the samples under PDS 7.1, but I've not done extensive
- testing in PDS, nor have I taken advantage of any PDS specific features
- (LangWin source compiled clean under PDS the first time with no changes
- required between the two flavors - except changing VARPTR to SSEG for variable
- length strings). Beginning with LangWin V2.3, the compressed set of
- distribution libraries were too large to fit on a 360K diskette. So I decided
- to omit the PDS libraries (since most people have QuickBASIC). If the
- distribution of LangWin that you have does not include the PDS libraries
- (LANGWINP.*), and you would like the PDS versions, please contact me and I'll
- send you a copy.
-
- VBDOS? Serious competition for LangWin, but no comparison when you consider the
- price (LangWin is free). I did load the sample programs and LangWin source into
- the VBDOS environment and ran them successfully (no modifications were
- required except for the VARPTR to SSEG changes made for PDS). I have not
- included a VBDOS compatible quick library with this distribution. I'd be
- flattered by anyone requesting a VBDOS compatible LangWin quick library, but in
- that case: save the cost of VBDOS, keep QB or PDS, and use the quick libraries
- included with this release. If you're gonna use VB, go ahead and use the GUI
- tools that come with it (I'm considering some VB-like interactive graphical
- tools for LangWin 3.0).
-
- Windoze? LangWin's mouse interface will not work properly if DOS is run in a
- window (not really a LangWin restriction, but the Int 33h interface to standard
- DOS mouse drivers does not work in Windows which has its own mouse driver). So
- if the code you develop with LangWin will be run in a Windows environment, and
- you want to take advantage of LangWin's mouse interface, then you must load a
- standard DOS mouse driver when you boot (via CONFIG.SYS or AUTOEXEC.BAT) BEFORE
- starting WIN, and your code must run in full screen DOS under Windows, it
- cannot run in a DOS window. This should not be a major restriction, but if you
- must run in a DOS window, consider VBWIN for your development.
-
- When using the LangWin quick library with the QB environment to develop code,
- the more free (as opposed to installed) main memory you have the better. QB
- consumes a certain amount of main memory when run. LangWin uses dynamic string
- arrays and many string variables. Between the memory consumed by the QB
- environment itself, and LangWin's strings, it is possible to encounter an "out
- of string space" or "out of memory" message while developing under QB with the
- LangWin library. (I developed LangWin in OS/2's DOS box which only had 470K
- free on my system and often ran out of string space. When booting DOS, which
- had 570K free, I did not have a problem).
-
- You may not see a string space problem while developing unless your programs
- get very large, have lots of comments, or use lots of strings. This is a
- QuickBASIC environment restriction and relates to how much main memory QB
- itself takes while loaded, and how it stores and manages strings. It's a good
- idea to free up as much main memory as possible before calling the QB
- development environment. Use the FRE (-1) and FRE ("A") commands from QB's
- immediate window to determine the amount of free memory you have left from
- within the QB environment. If you get an "out of string space" or "out of
- memory" message while developing in the QB environment, use CLEAR in the
- immediate window to reset all variables. If this does not help, save your code,
- exit QB, and start QB again. If you still get "out of memory" or "out of string
- space" try the following: reduce value of MaxWindows, reduce the "rows"
- parameter in your WIDTH command, reduce the value of MaxTextWins, UNLOAD the
- WINHELP module if it was loaded for online help (see the DOING "BASIC" WINDOWS
- Section for more details). Programs compiled with the LangWin library will be
- large (over 60K), but should not have a string space problem while running (of
- course this too will depend upon how big your EXE is and how much free memory
- you have).
-
- LangWin allows up to 16 attributes (0-15) to be specified for colors. In order
- to obtain 16 unique background attributes (usually there are only 8), blinking
- foreground colors are disabled. This requires that foreground/background
- attribute numbers be translated before using the COLOR command. Thus, when
- using LangWin, your should NOT use the COLOR command directly in your code to
- set foreground/background attributes. Instead, use LangWin's SetColor routine
- (load WINHELP.BAS into QB to see details on all LangWin's routines). SetColor
- takes two parameters (foreground and background attributes, between 0 and 15).
- It will translate these appropriately and issue the COLOR command itself. If
- you use the COLOR command instead of SetColor, you may get some unexpected
- colors. Also see Section 6.15 for more information on colors in LangWin.
-
- As of LangWin Version 1.2, a significant improvement in the speed of window
- creation (open) and deletion (close) has been achieved by writing directly to
- the video buffer (DEF SEG=&HB800). While the increase in speed is a benefit,
- there are some disadvantages in writing directly to the buffer (rather than
- using the standard DOS services). For example, if the location of the video
- buffer were to move, LangWin would not know it and over-write whatever did
- exist in the segment located at &HB800. Since many software products write
- directly to the video buffer to improve performance, it's not too likely that
- future releases of DOS will deliberately move the buffer. However, with DOS 5.0
- (and several other memory management products), one can reuse upper memory
- blocks (for TSRs, etc.) in order to free up as much of the first 640K as
- possible. I haven't tried it, but I suppose it's possible to reuse the block
- located at segment &HB800. In any case, if you do reuse the block at segment
- &HB800, LangWin 1.2 (or later versions) will not work.
-
-
-
- -------------------
- 2.0 GETTING STARTED
- -------------------
- Before you can begin developing with the LangWin quick library, you must place
- LANGWIN.BI (include file), LANGWIN.QLB and LANGWIN.LIB (libraries) on your
- hard disk, and tell QB where these files are (via the Options; Set Path menu).
- I recommend placing them in the same directory as your other QuickBASIC
- include files and libraries. LANGWIN.QLB contains QB.QLB and LANGWIN.BI
- contains QB.BI. If your code defines a user data type as RegType or RegTypeX
- and calls INTERRUPT or INTERRUPTX, you won't need to do anything extra beyond
- using LangWin.
-
- With V2.0, LANGWINP.QLB and LANGWINP.LIB are corresponding files for PDS;
- LANGWIN.BI can be used for both QB and PDS. If you are using LangWin with PDS,
- the following instructions also apply, just issue the corresponding PDS
- commands, etc.
-
- Select Options from the QB main menu, and Set Paths from the Options menu.
- You'll see the current paths for executable, include, library, and help files
- (these may all point to the same directory). You can then quit QB and place the
- corresponding LangWin files into your defined directories for include files and
- libraries. If your QB environment has no paths defined, then update the Set
- Paths menu and place LangWin's file and libraries into the corresponding
- directories. (For a quick start, just place the LangWin files in a working
- directory and call QB /ah /L langwin from that directory.)
-
- Once you have told QB where the LangWin files are (you'll only need to do this
- once), then call the QB environment from your DOS prompt as follows (I assume
- that "qb" is the name of your QB.EXE file and the directory where it resides
- is on your DOS PATH):
-
- qb /ah /L langwin (qbx /ah /L langwinp for PDS)
-
- The /ah parameter tells QB to allow dynamic arrays > 64K in size
- (it takes a lot of memory to save screen contents when opening windows).
-
- The /L langwin parameter tells QB to load the LangWin quick library
- (see your QuickBASIC manual for more information on Quick Libraries).
-
- See Section 3.1 (and the sample programs distributed with LangWin) for typical
- initialization code needed for LangWin.
-
- I've included the contents of QB.BI in LANGWIN.BI and the contents of QB.QLB in
- LANGWIN.QLB, so you DO NOT need to load the QB versions of these files
- (similarly for the corresponding PDS libraries, they are included in LANGWIN.BI
- and LANGWINP.QLB). If you used QB.BI and QB.QLB to call interrupts from your
- QuickBASIC programs, all you'll need is the LangWin files and you'll still
- be able to call interrupts.
-
- -------------------------
- 3.0 DOING "BASIC" WINDOWS
- -------------------------
- Once you have called the QB environment with the LangWin library, you're ready
- to begin doing windows. I'll cover "basic" windows in this section, and more
- advanced techniques in a later section.
-
- Note that most references to LangWin's routines in this document do not include
- a complete description of the corresponding parameters and return codes. This
- document attempts to show the concepts necessary to create a GUI environment
- using LangWin. For a detailed description of every LangWin routine, including
- parameters and return codes, load WINHELP.BAS (new with V2.0; included on your
- distribution disk) into QB and hit F2. You can select any LangWin routine and
- examine corresponding documentation. In order to reference the online help
- routines, first OPEN an existing module under development (or create a new
- one). Then select LOAD and load the WINHELP.BAS module (don't forget to UNLOAD
- the WINHELP module before compiling your code!). While developing, if you need
- to reference documentation for LangWin, just hit F2 and select the routine you
- need. Routine names in WINHELP will end with a period, this is only to avoid
- conflicts with the "real" routine names in LangWin. When you code the routine's
- name, do not include the period. If you get "out of string space" or "out of
- memory" errors, you may have to UNLOAD the WINHELP module.
-
- 3.1 Initialization
- ------------------
- Before you can call LangWin's routines, they must be DECLARED, global arrays
- and variables must be initialized, the screen must be set to text mode, and
- the mouse (if it exists) must be initialized. Here's some sample code to
- accomplish this (just copy this to start a program that will use LangWin):
-
- '===========================================================================
-
- '$DYNAMIC make all arrays dynamic
-
- DEFINT A-Z
-
- ' optional: test to see if color EGA or VGA monitor is present.
- ' see Section 6.16 for techniques to accomplish these tests.
-
-
-
- '$INCLUDE: 'LANGWIN.BI' ' TYPE, DECLARE and COMMON definitions for LangWin.
- ' NOTE: LANGWIN.BI contains all definitions found
- ' in QB.BI, so include for QB.BI is not needed.
- ' (similarly for QBX.BI in PDS).
-
- ' the following sets the stack at 9000 bytes. if the stack is not large enough
- ' you'll get some very strange errors. 9000 bytes should be large enough,
- ' but if you are in doubt, print the value of FRE(-2) to dynamically determine
- ' available stack space while your program is running.
-
- CLEAR , , 9000 ' set stack to 9000 bytes
-
-
- ' the following is not required by LangWin, but will let you determine
- ' the screen colors at the time your program was called, and re-establish
- ' those same colors after your program terminates.
-
- ' get attribute from current screen so it can be restored upon exit
- OrigAttr = SCREEN(1, 1, 1)' save original attribute from row 1, col 1
-
-
- ' if WIDTH command is used, it must be placed BEFORE call to LangWinInit.
- ' code in LangWinInit extracts max rows/cols from screen and saves
- ' in global variables (MaxCols and MaxRows). if WIDTH is used to change screen
- ' after LangWinInit has determined the screen's row/col dimensions, your
- ' windows won't work!
-
- WIDTH 80, 25
-
- ' these variables MUST be defined BEFORE call to LangWinInit.
- ' i've used sample values to define these variables.
- ' you should change these values to meet the requirements of the program
- ' you are writing, BUT keep these as low as possible
- ' to conserve memory at run time (see comments below for explanations).
-
- MaxWindows = 5 ' max simultaneous open windows
- MaxButtons = 10 ' max number of objects (including non-scroll text) active
- ' following two variables are new with V2.0:
- MaxTextLines = 35 ' maximum number of text lines in any scrollable win
- MaxTextWins = 3 ' max windows that can have scrollable text
- ' must be <= MaxWindows
-
-
- LOCATE , , 0 ' start with hidden text cursor
-
-
- ' LangWin only supports text mode. You MUST call the SCREEN 0 command BEFORE
- ' the call to LangWinInit. You can call SCREEN with a video page other than 0
- ' (i.e., SCREEN 0,,x,x where x is a page number supported by your system).
- ' Code in LangWinInit will determine which video page you are using and save
- ' the value in a global variable for use by other LangWin routines. If you
- ' call SCREEN 0 after LangWinInit and change the original video page, you'll
- ' get unpredictable results (i.e., LangWin will write to the original video
- ' page). However, you can use other video pages for functions not associated
- ' with your LangWin windows; just be sure to set the video page back to the
- ' original value defined below.
-
- SCREEN 0,,0,0 ' LangWin ONLY supports text mode
- ' You MUST call the SCREEN command BEFORE LangWinInit
-
-
- CALL LangWinInit ' initialize (if mouse exists, it will be displayed)
-
- ' if you get "subscript out of range" error while
- ' in LangWinInit, be sure you called QB with /ah.
- ' then try reducing the value of MaxWindows.
- ' check the WIDTH command; reduce number of rows.
-
-
- ' display "wallpaper" (background for windows)
-
- IF HaveMouse THEN CALL HideMouseCursor ' first hide mouse pointer
- CLS
- CALL SetColor(4, 15) ' pick any colors you like (run WINCOLOR for samples)
- FOR i = 1 TO MaxRows ' paint the screen
- LOCATE i,1
- PRINT STRING$(80, 178); ' can try 176, 177, or 178 to get different effects
- NEXT
- IF HaveMouse THEN CALL ShowMouseCursor ' display the mouse pointer
-
- '====================
- ' YOUR CODE GOES HERE
- '====================
-
- IF HaveMouse THEN HideMouseCursor ' we're done with the mouse
-
- ' now restore original screen colors before terminating
- bbb = (OrigAttr AND &HF0) \ 16 ' mask & shift to get original background
- fff = OrigAttr AND &HF ' mask to get original foreground
- PALETTE ' restore original palette
- CALL SetColor(fff, bbb) ' restore orig foreground/background
- CLS
- LOCATE , , 1 ' make text cursor visible
- END
-
-
- _____________________________
- Some notes on the above code:
- 1) LANGWIN.BI also contains the definitions found in QB.BI, so you won't need
- a separate "include" for the QB.BI file. LANGWIN.QLB also has the contents
- of QB.QLB so you won't need that quick library either. For PDS users,
- LANGWIN.BI covers QBX.BI and LANGWINP.QLB covers QBX.QLB.
-
- 2) LANGWIN.BI contains all of the COMMON statements for LangWin's global
- variables. You should examine these variable names so that you don't
- mistakenly re-use the same name in your code (which will cause
- unpredictable results).
-
- 3) LangWin will support both 80 and 40 columns, as well as 25, 43, and 50
- rows (as long as your hardware supports them). You can use the WIDTH
- command to set rows and columns; however, the WIDTH command MUST come
- before the call to LangWinInit (which tests the screen and sets global
- variables with current rows/columns on the screen). WARNING, using 43 or
- 50 rows will increase the memory requirements for your programs and may
- result in "out of memory" errors while developing.
-
- 4) PRIOR to calling LangWinInit, you MUST set MaxWindows to the maximum
- number of windows that can be opened on the screen at any one time. Keep
- this value a small as practical. It is used to DIM several arrays, and a
- large value can quickly use up all of the QB environment's free memory (in
- this case, you'll get an "out of memory" or perhaps "out of string space"
- message while developing). Use small values while developing in the QB
- environment. If your production code will need a larger value, set it just
- before creating your EXE file. If your program attempts to open more
- windows than defined by MaxWindows, the window will not open (you'll get a
- negative return code from the routine called to open the window).
-
- 5) PRIOR to calling LangWinInit, you MUST set MaxButtons to the maximum
- number of buttons, check boxes, input fields, titles, and lines of
- non-scrollable text lines that can be active on all visible windows. Like
- MaxWindows, this should be kept small during development and increased to a
- practical value just before creating your EXE file. A large value of
- MaxButtons has less of a memory impact as MaxWindows. Thus, if you have a
- memory shortage while developing, reducing MaxWindows will buy back more
- storage. If your program exceeds MaxButtons, new objects will not be
- created (you'll get a non-zero return code from the routine called to
- create the object - if you don't test return codes, you'll notice that
- expected objects are missing from some windows). New with V2.1: non-
- scrollable lines of text (created with ShowWinText) are saved in LangWin's
- data structures (see section 5.0) so they can be re-displayed when a window
- is resized. Thus, if you are converting from a previous version of LangWin,
- MaxButtons will have to be increased from it's previous value (by the
- number of lines of non-scrollable text and titles that can be active on all
- visible windows). Space in the data structures is used dynamically. As
- windows are opened and objects created, space is used. As windows are
- closed, space occupied by objects is freed. So, MaxButtons needs only to be
- large enough to store the maximum number of objects, titles, and static
- text lines that would be visible on all windows open at any one time (this
- includes all windows opened statically BEFORE the WinEvent loop, AND
- windows opened dynamically WITHIN the WinEvent loop in response to user
- actions).
-
- 6) PRIOR to calling LangWinInit, you MUST set MaxTextLines (new with V2.0) to
- the maximum number of scrollable text lines in any window. When you open a
- window to contain scrollable text (OpenScrollWindow), one of the parameters
- passed is a string array containing your scrollable text. LBOUND of this
- array MUST be 1; otherwise, OpenScrollWindow will fail with a run-time
- error. If the string array with your scrollable text is built dynamically
- at run-time, then you must test to determine if the size exceeds
- MaxTextLines. If this conditions occurs, then OpenScrollWindow will only
- display the first MaxTextLines of text. Your program will have to take
- appropriate action to insure that the user will get to see all lines of
- text (e.g., give the user the option to see more and if chosen, delete
- the first MaxTextLines of text, loop, and open a new window with
- scrollable text. Continue until all text has been seen). The contents of
- the string array passed to OpenScrollWindow is moved into a global array
- called SaveText(i,j) where UBOUND(SaveText,2)=MaxTextLines. Upon returning
- from OpenScrollWindow, you can ERASE the string array (since the scrollable
- text will have been saved in SaveText). This will help conserve memory.
- Of course, if UBOUND(Text$) > MaxTextLines, then you must insure that the
- user sees all of the Text$ array before you ERASE it.
-
- 7) PRIOR to calling LangWinInit, you MUST set MaxTextWins (new with V2.0) to
- the maximum number of simultaneous windows that can have scrollable text.
- All scrollable text is saved in SaveText(i,j) where UBOUND(SaveText,1) is
- MaxTextWins. The value MUST be less than MaxWindows and (to conserve
- memory) should be as low as possible.
-
- 8) LangWinInit sets constants, initializes all global variables, DIMs all
- arrays, and initializes and displays the mouse (if it exists). The
- constants TRUE (-1) and FALSE (0) are defined in LangWinInit and can be
- referenced in your code as needed.
-
- 9) The global variable HaveMouse is set by LangWinInit and can be tested in
- your program to determine if a mouse exists. HaveMouse is set to TRUE if a
- mouse exists; else, it is set to FALSE.
-
- 10) Before writing anything to the screen, you must hide the mouse pointer (if
- a mouse exists). When a mouse pointer is on the screen, and a character is
- written to the same position, the character will not be displayed
- correctly. If a mouse exists, hide the pointer, write to screen, and then
- show the pointer. This technique is used when writing "wallpaper" on the
- screen.
-
- 11) While not necessary for windows, placing "wallpaper" on the screen for
- background provides a nice contrast. ASCII characters 176, 177, or 178 can
- be used for a nice effect.
-
- 12) Throughout ALL of your code, you MUST use the SetColor routine instead of
- the COLOR command to change color attributes. The parameters are the same
- (foreground and background attributes) as the COLOR command. LangWin uses
- BIOS interrupt 10h, function 10h, sub-function 03h to disable blinking
- colors. This allows 16 attribute numbers for window (background) colors
- (rather than just 8). However, with blinking disabled, foreground and
- background attribute numbers MUST be translated before calling BASIC's
- COLOR command. SetColor does this translation and issues the COLOR command
- with the proper values. If BASIC's COLOR command is called without this
- translation, you will get unexpected colors.
-
- 13) You MUST use the SCREEN 0 command BEFORE calling LangWinInit (V2.3). You
- can use any video page supported by your system. LangWinInit saves the
- video page you select in a global variable which is used by other routines
- to read/write data in the video buffer. Thus, once you select a video page
- in the initial SCREEN command (which must be BEFORE the call to
- LangWinInit), you should not change the video page in a subsequent
- call to SCREEN. In general, there's no need to have another call to SCREEN.
- One exception: for functions not associated with LangWin, you could use
- SCREEN to change to a new page, display information on the screen, then
- use SCREEN to change back to the original page number used in the
- initial SCREEN command. For example, you could use LangWin to display
- a scrollable list of filenames in one page. When a file name is clicked,
- you could use SCREEN to change pages, SHELL to DOS and call a viewer to
- display the selected file. Upon return from the SHELL, use SCREEN to
- change back to the original page (containing the screooable list). In this
- case, be sure to test for a mouse and hide it before SHELLing and restore
- it afterwards.
-
- 14) I used the CLEAR command to initialize the stack at 9000 bytes. To save
- memory and improve performance, LangWin is not compiled with the run-time
- debug option. Thus, you won't get an explicit warning if you exceed the
- stack space. Instead, you will get "strange" errors. In one case, the
- errors did not show up while developing in the QB environment. However,
- when testing with compiled code, strange things began to happen when the
- compiled program terminated. Characters no longer appeared on the screen,
- my machine locked up, or re-booted itself. If you suspect that you may be
- exceeding the stack space (usually caused by many recursive calls to
- subroutines/functions, or just many subroutines/functions with many
- parameters), print the value of FRE(-2) dynamically in your program
- to determine stack space capacity and increase as necessary.
- If FRE(-2) shows plenty of stack space free, you can reduce the size of
- the stack or even omit the CLEAR command entirely.
-
- 3.2 Some Simple Windows
- -----------------------
- Now, let's get on with creating some windows. LangWin requires a technique
- called event-driven programming. (For those with Version 1.x of LangWin, the
- event loop technique has been changed as of V2.0; you'll need to change your
- program's logic).
-
- In general, for event-driven programming you must write code segments to handle
- every possible event in every possible window, whether or not the window has
- been opened yet (i.e., you must not only consider static windows that you open
- when the program initializes, such as a main menu, but also any dynamic window
- that will be opened "on the fly" based upon what object the user clicks, such
- as an error window or sub-menu window). An event is defined as any
- button/icon/text click. Your program consists of one main loop (new with V2.0;
- previous versions required other loops), where you first call a special LangWin
- routine (WinEvent) that will process all events. When an event occurs, the
- special routine will return control to your program which must then determine
- the exact window and event that occured, and execute a code segment dedicated
- to handling that combination of window/event. In event-driven programming, the
- event "drives" the code segment dedicated to processing that event. Your
- program essentailly waits for an event (i.e., waits for control to be returned
- from WinEvent), drives the corresponding code, and continues looping (until
- some event occurs that signals termination).
-
- A high level view of your program's logic would be:
-
- open static window(s); save window numbers in unique variables
- create buttons, input fields, click boxes as needed; save handles in
- unique variables
-
- do while any window is open
- wait for an event to occur in any window (call WinEvent)
- determine which window had focus
- determine which event occurred in the window that had focus
- process the window/event combination (could open dynamic windows)
- loop
-
-
- As you can see, once static window(s) have been opened, you will loop: waiting
- for an event, processing the event, and continuing the wait/process cycle as
- long as any window remains open. Each possible window/event combination will
- have a separate code segment dedicated to processing that event. Among the
- actions that could be taken within the code segment, a dynamic window with its
- own objects (buttons, click boxes, input fields, etc.) could be opened. If this
- is done, then your program would also need a code segment to handle events in
- this (and all other possible) dynamic window(s).
-
- The LangWin routines needed to accomplish the above tasks are:
- (these are just a sub-set of all routines in LangWin, see WINHELP.BAS
- for complete documentation):
- BlankWin - opens a plain window; returns window number
- OpenScrollWindow - opens a window with scrollable text; returns number
- MakePushButton - creates a push button; returns its handle value
- MakeCheckBox - creates a check box; returns its handle value
- MakeInputField - creates an input field; returns its handle value
- WinEvent - processes all GUI activity within all open windows.
- returns a value that identifies the number of
- the window with focus (see Section 5.7 for more
- information on window numbers), and returns a
- parameter that defines the event that occurred
- (1=close; 2=text line selected; 3=button selected).
- CloseWindow - close the current window
- ShowWinText - places static (non-scrollable) text into current
- window
-
-
- See Section 6.7 for additional details on event-driven programming techniques.
-
-
- -----------------------
- 4.0 USING LANGWIN'S GUI
- -----------------------
- Before describing LangWin's data structures and providing more advanced
- techniques for doing windows, a brief description of how to use the GUI
- created by LangWin will be presented.
-
- Windows created by LangWin have the following standard GUI features:
-
- 4.1 Close Icon
- --------------
- The upper left corner of each window can optionally have a close icon (this is
- determined by the CloseIcon parameter in the BlankWin and OpenScrollWindow
- routines, see WINHELP.BAS). Double click on this icon and WinEvent will return
- an action code of 1 to your program. Regardless of whether a close icon is
- displayed, hitting the ESC key will return an action code of 1 to your program
- (if you are in an input field, ESC will exit the input field; hit ESC again if
- you want to close the window. See Input Fields below for details). You can
- choose to ignore the close event in your program, and instead require a
- specific button click to effect the window being closed.
-
- 4.2 Moving, Resizing, and Minimizing the Active Window
- ------------------------------------------------------
-
- 4.2.1 Moving
- ------------
- Windows can be moved to any position on the screen (as long as the window is
- marked as movable). Using the left mouse button, click and hold the close icon
- (if there is no close icon, click and hold the upper left corner of the
- window). After a moment (in this case, a "moment" equals approximately 2/9
- second), the mouse pointer will change to a pair of "corner pointers": one in
- the upper left corner of the window and one in the lower right. (If the mouse
- pointer does not turn into "corner pointers", then the window is not movable -
- see description of OpenScrollWindow and BlankWin for details on how to mark a
- window as unmovable). While holding the mouse button down, drag the pair of
- "corner pointers" to a new position and release the mouse button. Don't forget
- that if the window has a shadow, the new position must have room for the
- shadow (the "corner pointers" only show the extent of the window, not it's
- shadow which takes two columns on the right and one row below the window).
- When moving a window, the mouse cursor is limited such that you won't be able
- to move the window off of the screen (if the window has a shadow, the cursor
- limit will leave room for it).
-
- See the WinColor parameter in BlankWin and OpenScrollWindow for details on how
- to mark a window as unmovable. See the BorderColor parameter (in the same two
- routines) for details on making a window shadowless.
-
- 4.2.2 Resizing
- --------------
- Using the RIGHT mouse button, click and hold any of the window's corners. After
- a moment (2/9 sec) the mouse pointer will change into a "corner pointer". (If
- the mouse pointer does not change, then the window is not resizable - see
- OpenScrollWindow and BlankWindow for details on how to mark a window as not
- resizable). Drag the corner pointer to a new position and release. The window
- will be resized appropriately. LangWin imposes certain limits when shrinking
- windows. If the window has a scrollable text area, the window cannot be made so
- small that the text area would disappear (the smallest that the text area can
- be is one character). In this case, the resulting size of the window containing
- scrollable text will depend upon the size of the borders (within the window)
- around the scrollable text area (which can be anywhere within a window - see
- OpenScrollWindow). If the window does not have scrollable text, then the
- smallest it can be is three characters by three characters (including the
- characters that make up the border).
-
- When a window is expanded or contracted, the text area (if present) and all
- buttons, input fields, check boxes, titles, and plain text (non-scrollable)
- will be moved proportionally. When a window is contracted, these objects will
- be closer together and may appear "squished". If the window is made small
- enough, some of these objects will DISAPPEAR (because they will no longer be in
- the visible area of the window). Making the window larger will cause these
- objects to re-appear. However, some objects WILL disappear altogether when a
- window is resized. These include horizontal lines (see MakeHorizLine), vertical
- lines (MakeVertLine), and boxes (MakeBox) - also see Section 6.1. If this
- presents a problem (i.e., you NEED to have these objects in your window, and
- having them disappear when your end user resizes the window is not acceptable,
- then mark the window as not resizable when it's opened).
-
- See the BorderColor parameter in BlankWin and OpenScrollWindow for details on
- how to mark a window as not sizable.
-
- HINT: when a window is resized, it's objects are redrawn (at new positions) in
- the same order in which they were created. If the window gets too small, some
- adjacent objects (in the original window) may be redrawn in the same space (in
- the smaller window) and overlay one another. In this case, the last object
- redrawn will appear "on top" of others (and may even completely hide others
- that are "beneath" it). Thus, for adjacent objects in the original window, the
- object want to appear "on top" of others (when a window is made smaller) should
- be defined last. For example, as the window is made smaller, non-scrollable
- text (see ShowWinText) in the window's second row and the window's title (see
- ShowTitle) in first row might both be placed on the first row of the window. If
- you want the title to appear "on top" of the non-scrollable text in this case,
- then place the CALL ShowTitle after all CALL ShowWinText (for text in the given
- window).
-
- HINT: a window with scrollable text can be opened without a scroll bar (for
- example, when items in a scrollable list are used to select sub-menus, and you
- know that the entire list will fit into the original window, then you might
- choose to specify no scroll bar for the window). If you shrink this window to
- the point where all of the scrollable text will not fit, then some scrollable
- text (i.e., your menu items) will not be displayed. In this case, a scroll bar
- will NOT be automatically included when the window is shrunk (because the
- original specification to omit a scroll bar will take precedence). If you must
- open a window with scrollable text and specify that that it not have a scroll
- bar, then I'd recommend making the original window not sizable (see BorderColor
- parameter in OpenScrollWindow routine).
-
- HINT: if a window is marked as unmovable, it cannot be resized (regardless of
- the state of the resize flag).
-
- 4.2.3 Minimizing
- ----------------
- Place the mouse cursor ANYWHERE in the window, then click and hold BOTH left
- and right buttons. After a moment (2/9 sec), the window will be contracted to
- its minimal size (by simulating the process of dragging the lower right corner
- to the upper left as far as possible - see previous section for a discussion on
- minimal size). Repeating this process on a window that has been minimized will
- (in most cases) expand the window to its original size (by simulating the the
- process of dragging the lower right corner back to the original position).
-
- Both resized and minimized windows can be moved (left click on upper left
- corner and drag). If a minimized window is moved closer to the bottom row or
- right edge of the screen, then clicking with both buttons to expand back to
- original size would result in the window extending beyond the screen's
- boundaries. In this case, the window will be expanded as large as possible (but
- may indeed be smaller than its original dimensions).
-
- See the BorderColor parameter in BlankWin and OpenScrollWindow for details on
- how to mark a window as not sizable (which means it cannot be minimized).
-
- HINT: if the window's size has been changed (by dragging a corner with the
- right button), and you want to return the window to its original size, first
- click with both buttons to minimize, then click with both buttons again to
- expand back to original size. Of course, you could also drag a corner with the
- right button to resize back to original dimensions (unless you don't remember
- what original window looked like).
-
- HINT: here's a technique I've found for clicking both buttons to minimize.
- Place the mouse pointer on the window to be minimized (but not on a corner).
- Click the right button, then click the left button. The point is that the right
- button should be clicked first (so that just in case you happen to be on a
- button or some other object, no action will be taken), then the left button is
- clicked. You don't have to actually click both simultaneously; they both just
- have to be down. I found that trying to click both simultaneously sometimes
- actually results in the left button being clicked first. In this case, if the
- pointer happens to be on and object, that object will be selected. Yes, I could
- have used another icon on the window's border to implement minimizing; I just
- liked the idea of being to minimize with the mouse cursor anywhere on the
- window in question. My apologies to those who dislike the right mouse button
- (but as long as it's there, I thought I'd use it).
-
- HINT: if a window is marked as unmovable, it cannot be resized (regardless of
- the state of the resize flag), and thus it cannot be minimized.
-
-
- 4.3 Focus
- ---------
- Before discussing buttons, check boxes, input fields, and scrollable text, the
- concept of focus must be reviewed. Focus has taken on additional meaning with
- V2.0 and later. A window can have focus (i.e., it's the current window), and an
- object within the current window can have focus.
-
- When WinEvent returns control to your program, the value of the WinEvent
- function corresponds to the number of the window that had focus when the event
- occurred. The value of WinEvent's parameter is a code for the type of action
- that occurred in the window with focus. You will need to determine which line
- of text or button caused that event (i.e., had focus when the event occurred).
- Clicking anywhere on a window gives it focus. Clicking on a window's object
- (i.e., button, check box, input field, or scrollable text) gives the owning
- window and the corresponding object focus (input fields will display a cursor
- when they have focus, other objects will be displayed in reverse video). Note
- that if scrollable text exists, one line ALWAYS has focus. If buttons, check
- boxes, and input fields exist, one of them usually has focus (but it is
- possible for none of the buttons, check boxes, or input fields to have focus,
- see Changing Focus below). Thus, it is possible for BOTH a line of text AND a
- button to have focus when WinEvent returns to your program. See Section 6.6 for
- details on how to distinguish between the line of text with focus and the
- button with focus when WinEvent returns control to your program.
-
-
- 4.4 Changing Focus
- ------------------
- With LangWin 2.0, you can mouse to any visible window and select an object.
- Thus, you'll need to understand the difference between the current window with
- focus and the current object in a window with focus. There are actually two
- kinds of focus: "window focus" - the current window; and "object focus" - the
- current object in a window. Clicking on a window gives it "window focus".
- Clicking on an object in a window gives "window focus" to the selected window
- and "object focus" to the selected object. The following two sub-sections will
- help to clear up these concepts.
-
- 4.4.1 Window Focus
- ------------------
- Only one window can have focus. If your program creates a window (with BlankWin
- or OpenScrollWindow), then it has window focus. Until that focus is changed,
- any subsequent objects created with "make" routines will be associated with
- that window. Window focus can be changed by clicking anywhere on any visible
- window. That window will be moved to the "top" of the stack of overlapping
- windows and given focus (in addition, if an object in that window was clicked,
- it will be given object focus - see next section). Pressing CTRL and PgUp will
- also change the window in focus (the "bottom" window on the screen is moved to
- the "top" and given focus). Repeated use of CTRL-PgUp will cycle through all
- visible windows. If the windows displayed are overlapping, you'll easily be
- able to tell which window has focus (it will appear on "top" of the stack). If
- the windows do not overlap, there won't be a visible difference between the
- window with focus and all others. If you use the keyboard to control the GUI
- (see Section 4.10), then keyboard commands (such as TAB, ENTER, etc) take
- effect only in the window with focus.
-
-
- 4.4.2 Object Focus
- ------------------
- Every object in a window can also be given focus. Typically, just clicking on
- the object will give it focus and it will be displayed in reverse video (when
- input fields have focus, a text cursor appears in the field). While it is
- possible for no button or check box to have focus, if there is scrollable text
- in the window, then one line will ALWAYS have focus (one exception: null lines
- in scrollable text can have focus, but they will be invisible and appear as if
- no scrollable text has focus). There are several ways to change focus. Clicking
- on any object will give it focus. If a line of scrollable text is clicked, then
- it is given focus (and focus is removed from any button or check box previously
- having focus). If a button, check box, or input field is clicked, then it
- obtains focus (and the text focus is unchanged). Thus, if both scrollable text
- and other objects exist in a window, there will always be one line of text with
- focus, and there may or may not also be another object with focus.
-
- In addition to clicking on an object to give it focus, the TAB and Shift-TAB
- keys can be used to change the focus of buttons, check boxes, or input fields
- in the current window (text focus is not affected by TAB). Hitting TAB will
- move focus to the next button, check box, or input field (if scrollable text
- exists, one line ALWAYS has focus, and this focus remains unchanged by the TAB
- keys. Text focus is only changed by mouse or scrolling). Shift-TAB will move
- the focus to the previous button, check box, or input field. After cycling
- through all buttons, check boxes, and input fields, the focus will disappear
- (except for the line of scrollable text with focus). Hitting TAB or Shift-TAB
- will cause the focus to reappear on the next/previous button, check box, or
- input field.
-
- Unless an input field is being actively edited (see Input Field), the
- left/right arrow keys will function like TAB/Shift-TAB for changing focus. The
- up/down arrows change text focus, scrolling the list as appropriate.
-
- If scrollable text does not exist, then the up/down arrows will also function
- like TAB/Shift-TAB for changing focus.
-
- When you hit ENTER, the object with focus is selected (just as if it were
- clicked with a mouse). If BOTH a line of scrollable text AND an object
- (i.e., push button or check box) has focus, then an action code of 3 will
- be returned from WinEvent (i.e., the object takes precedence). In this case,
- your code can check WinParms(CurWinPtr,16) to get the handle of the button
- pushed and WinParms(CurWinPtr,18) and WinParms(CurWinPtr,15) to get indices
- in SaveText that correspond to the line of text currently in focus (see Section
- 5.8).
-
-
- 4.5 Scrolling Text
- ------------------
- The right side of each window can optionally show a scroll bar, slider, and
- up/down arrows. These will not be present if a plain window was opened
- (BlankWin), or if you specifically open a window with scrollable text, but
- specify no scroll bar (see BorderType parameter in OpenScrollWindow). If the
- scrollable text array will completely fit into the defined text area of the
- window (i.e., nothing to scroll), then the slider will not be displayed (scroll
- bar and arrows will still be visible). If a scroll bar exists, then WinEvent
- will handle all scrolling for you. Scrolling will change the text line that has
- focus (shown in reverse video). The user can click on the up/down arrows to
- scroll one line at a time. Click on the scroll bar (not the slider) and the
- text will page up/down (if the click was below the slider, page down; above the
- slider, page up). Drag the slider up/down, and the text will scroll to the
- correct relative new position of the slider when the mouse button is released
- (usual GUI stuff).
-
- The keyboard can also be used to scroll. Up/down arrows for single lines.
- Home/End keys for start/end of text, and PgUp/PgDown for paging.
-
- If the scrollable text has null lines, when one of these is given focus
- (clicking, arrows, scrolling, etc.), it will still appear as null (i.e.,
- invisible). This will make the scrollable text focus seem to disappear when
- selecting a null line. This could be confusing to the end user, and unless you
- have a specific use for it, I'd recommend that your scrollable text not include
- null lines. When OpenScrollWindow creates a list of scrollable text, all null
- entries that exist at the end of the text array are eliminated (null lines
- within the text array are not eliminated).
-
-
-
- 4.6 Selecting Scrollable Text
- -----------------------------
- The OpenScrollWindow takes a parameter that defines an array of scrollable
- text. This text is displayed in the window, and as previously described, can
- be scrolled up or down. Single clicking on a line of text will give it focus.
- Double-clicking on a line of text will give it focus, AND will return control
- from WinEvent with an action code of 2 (value of the WinEvent function will be
- the window's number where the text was clicked).
-
-
- 4.7 Push Buttons
- ----------------
- The MakePushButton routine is used to create push buttons in the current window
- (it returns a unique handle number associated with the button created). These
- buttons can optionally have drop shadows. Clicking on any push button will give
- it focus, AND will return control from WinEvent with an action code of 3 (value
- of the WinEvent function will be the window's number where the button was
- clicked). Depending upon the logic of your program, you can deactivate/activate
- individual push buttons. Deactivating the button will cause its text to clear
- (the button itself, and any shadow, is still visible, just its text is clear).
- Any subsequent clicks on a deactivated button will NOT produce any events.
- Activating the button will cause its text to be visible and click events to be
- recognizes. A button can be used to open a child window. If you do not want the
- end user to be able to click the button again while the child window remains
- open, then use the deactivate function. When the child window is closed,
- activate the button.
-
-
- 4.8 Check Boxes
- ---------------
- The MakeCheckBox routine is used to create check boxes in the current window
- boxes are toggle switches used to select (un-select) user defined options.
- Clicking on a check box will give it focus and change its state (from up to
- down, or down to up). There are no check box actions/events that would cause
- WinEvent to return control to your program; thus, there are no WinEvent action
- codes associated with check boxes. However, after control is returned to your
- program (for some other event), you can examine the state of all check boxes in
- the current window. WinEvent's return value will tell you the current window,
- and the code you write to handle that window's events should test each check
- box created in the window (by referencing the specific variables you used to
- save the handles of all boxes in that window). Depending upon the state of the
- check boxes in the current window, your code can take any necessary action (see
- DATA STRUCTURES and ADVANCED WINDOWS below).
-
-
- 4.9 Input Fields
- ----------------
- The MakeInputField routine is used to create input fields in the current window
- (it returns a unique handle number associated with the field created). An
- input field is an editable area that can be used for data entry. Tabbing to, or
- clicking on, an input field will give it focus (cursor will be visible) and
- make it available for data entry and/or editing. Input fields can be re-
- selected at any time to make changes/corrections as necessary. There are no
- actions/events from an input field that would cause WinEvent to return control
- to your program; thus, there are no WinEvent return codes associated with
- input fields. However, after control is returned to your program (for some
- other event), you can examine the contents of all input fields in the current
- window to see if they have been updated. WinEvent's return value will tell you
- the current window, and the code you write to process that window's events
- could test the current contents of each input field created in the window (by
- referencing data associated with each input field's unique handle). Your code
- would have to "remember" the previous contents to determine if the input field
- had been changed. Depending upon the current contents of the input fields in
- the current window, your code would take any necessary action (see DATA
- STRUCTURES and ADVANCED WINDOWS below). The following keys are available while
- in an input field:
-
- ESC - exit input field (useful if you have several input
- fields, but want your mouse pointer back immediately)
- current window retains focus
-
- Ctrl-PgUp - exit the input field (next WINDOW given focus)
- (new with V2.0)
-
- ENTER - exit input field (next object given focus)
- TAB - exit input field (next object given focus)
- DOWN Arrow - exit input field (next object given focus)
- Shift-TAB - exit input field (previous object given focus)
- UP Arrow - exit input field (previous object given focus)
- (the above 5 keys are useful if you have several input
- fields and want to go to next/previous field immediately
- after completing the current field - data entry)
-
- Ctrl-c - clear entire contents of the input field
- Ctrl-y - clear entire contents of the input field
-
- Ctrl-END - clear input field from cursor to end
-
- INSERT, BACKSPACE, LEFT/RIGHT Arrows, DELETE, HOME, END
- (the above keys should be self explanatory)
-
- While in data entry mode (within an input field), the mouse remains active. In
- addition to the Arrow keys, you can use the mouse to position the text cusror.
- In addition to the ESC key, you can use the mouse to exit an input field (just
- click anywhere outside the input field).
-
- An input field can easily be used for password entry. When defining the input
- field (via MakeInputField), use a negative value for its length (new with
- V2.2). This will force all text entered into the field to be displayed with
- the * character. Another technique (available in previous releases) for
- entering passwords would be to use the same value for both the foreground and
- background colors of the input field. In this case, nothing will be displayed
- when text is entered. For both techniques, the actual text entered (i.e., the
- password) is saved in ButtonsText(handle) - just as for any other input field -
- and could be used to validate the password. See DATA STRUCTURES for a
- description of the contents of ButtonsText.
-
-
- 4.10 No mouse
- -------------
- For those without a mouse (or those writing code that might run on systems
- without a mouse), LangWin's GUI functions can be selected via the keyboard. The
- Changing Focus Section (above) already described how to use the keyboard to
- change focus. To select the current object with focus, just hit ENTER.
-
- If a push button has focus, hitting ENTER will return control from WinEvent
- with a value of 3. (Note that even though a line of scrollable text ALWAYS has
- text focus, if a button has focus, hitting ENTER will return from WinEvent with
- a code of 3. In this case, if you need to know which line of text also had
- focus, then this can be done - see DATA STRUCTURES and ADVANCED WINDOWS for
- additional details).
-
- If a check box has focus, hitting ENTER will toggle the check box, but an event
- code will NOT be generated and control will remain within WinEvent.
-
- If an input field has focus, hitting ENTER will exit the input field and move
- focus to the next object (in current window). An event code will NOT be
- generated and control will remain within WinEvent.
-
- If ONLY a line of scrollable text has focus (i.e., no button, check box, or
- input field has focus), hitting ENTER will return control from WinEvent with am
- action code of 2. By hitting TAB, you will cycle the focus through all buttons,
- check boxes, and input fields, and eventually get to a state where the ONLY
- object with focus is a line of scrollable text (if no scrollable text exists,
- then focus disappears).
-
-
- ----------------------------------------
- 5.0 DATA STRUCTURES AND GLOBAL VARIABLES
- ----------------------------------------
- The following subsections describe LangWin's data structures and global
- variables (some of which have been added/changed with LangWin 2.0). You will
- need this information for the advanced window techniques presented in the
- Section 6.0. The global variables that are user definable could either be hard
- coded in your program or read from an INI file (so your end user can change
- them to meet system requirements and/or constraints).
-
-
- 5.1 MaxWindows
- --------------
- This global variable defines the maximum number of open windows that can appear
- on the screen at one time. If you try to open more windows than MaxWindows, the
- BlankWin or OpenScrollWindow routines will fail and return an error code.
- MaxWindows MUST be defined by you PRIOR to calling LangWinInit and SHOULD NOT
- BE CHANGED (see Section 3.1 for additional details).
-
- 5.2 MaxButtons
- --------------
- This global variable defines the maximum number of push buttons, check boxes,
- input fields, window titles, and lines of non-scrollable text that can be
- created on all possible open windows. If you attempt to create more objects,
- the corresponding "make" routine will return an error code. MaxButtons MUST be
- defined by you PRIOR to calling LangWinInit and SHOULD NOT BE CHANGED (see
- Section 3.1 for additional details).
-
- New with V2.1: non-scrollable lines of text (created with ShowWinText) are
- saved in LangWin's data structures (see section 5.0) so they can be re-
- displayed when a window is resized. Thus, if you are converting from a previous
- version of LangWin, MaxButtons will have to be increased from it's previous
- value (by the number of lines of non-scrollable text and titles that can be
- active on all visible windows). Space in the data structures is used
- dynamically. As windows are opened and objects created, space is used. As
- windows are closed, space occupied by objects is freed. So, MaxButtons needs
- only to be large enough to store the maximum number of objects, titles, and
- static text lines that would be visible on all windows open at any one time
- (this includes all windows opened statically BEFORE the WinEvent loop, AND
- windows opened dynamically WITHIN the WinEvent loop in response to user
- actions).
-
-
- 5.3 MaxTextLines
- ----------------
- This global variable defines the largest number of scrollable text lines that
- can appear in any window. If your program will open two windows, one with 25
- lines of scrollable text and another with 50 lines, then (in this case)
- MaxTextLines would have to be set to 50. MaxTextLines MUST be defined by you
- PRIOR to calling LangWinInit and SHOULD NOT BE CHANGED (see Section 3.1 for
- additional details). New with 2.0.
-
- If the number of scrollable text lines to be displayed in a window is not
- known during development, but will be determined dynamically at run-time, then
- you must take a best guess at a reasonable value for MaxTextLines.
- OpenScrollWindow will compare the UBOUND of the string array passed with
- MaxTextLines. If UBOUND(Text$) > MaxTextLines, then OpenScrollWindow will
- only display the first MaxTextLines of the array. In this case, the last
- line displayed will be set to the string: "(Incomplete List)"
- so your user will be aware that some data is missing. Your program should
- detect this condition (by comparing UBOUND(Text$) to MaxTextLines) and take
- appropriate action. For example, your could offer the user an option to
- see the missing text, and if selected, delete the first MaxTextLines from the
- Text$ array, loop, and open a new scrollable text window. Continue until
- all text has been displayed. Alternatively, you could just split the
- original Text$ array into smaller arrays (size <= MaxTextLines) and open
- a scrollable window for each. I'll leave the details to you!
-
- 5.4 MaxTextWins
- ---------------
- This global parameter defines the maximum number of windows that can be open
- with scrollable text. MaxTextWins MUST be <= MaxWindows (makes sense; can't
- have more text windows than windows of any kind, right!). MaxTextWins is
- used to dimension the global array SaveText which will hold all scrollable text
- in all open windows (see Section 5.8 for more details on SaveText array).
- MaxTextWins MUST be defined by you PRIOR to calling LangWinInit and SHOULD
- NOT BE CHANGED (see Section 3.1 for additional details on MaxTextWins).
- New with V2.0.
-
- 5.5 AnyWinOpen
- --------------
- With LangWin 2.0, this global variable is created, defined, and changed by
- LangWin's routines. It will be TRUE when any window is open and FALSE when all
- windows have been closed (DO NOT CHANGE THIS VARIABLE). You can use it to
- control the exit from your loop that checks WinEvent as follows:
-
- DO WHILE AnyWinOpen
- wnum=WinEvent(action)
- process the action in window # wnum
- LOOP
-
-
-
- 5.6 CurWinPtr
- -------------
- When a window is opened (via BlankWin or OpenScrollWindow), its parameters are
- stored in an available "slot" of the WinParms array (see Section 5.9). The
- "slot" in WinParms is called the window's handle. The handle of the current
- window with focus can always be found in the global variable CurWinPtr.
- CurWinPtr is created, defined, and updated by LangWin's routines (DO NOT DEFINE
- OR CHANGE IT).
-
-
- 5.7 Window numbers vs window handles
- ------------------------------------
- With LangWin 2.0, when a window is opened (via BlankWin or OpenScrollWindow),
- it is given a unique window number (i.e., the value returned by the function
- used to open the window). The window number should be saved in a unique
- variable name. The window's parameters are stored in the WinParms array. The
- "slot" in WinParms where the window's data is saved is called its handle.
- Window numbers are unique; window handles are not unique. Window handles are
- re-used after a window is closed (i.e., the "slot" in WinParms will be re-used
- to hold a new window's parameters).
-
- The window's handle cannot be used to uniquely determine what to do when
- control returns from WinEvent (i.e., after an event occurred in a window). The
- window's number, however, is a unique sequential value which is never re-used.
- The number of the window where an event occurred must be used to uniquely
- determine how to process events in that window. For example, handle number 3
- might point to data for window number 6 at one point, and after that window is
- closed and another is opened, handle number 3 could then point to data for
- window number 7. If the window's handle (3) were used to select code to process
- the event, you would not know exactly what to do because either window 6 or 7
- could have caused the event. Using the window's number, however, will tell you
- exactly which window caused the action, and thus you can select the proper code
- to handle events for that window (each window will have a different set of code
- to handle its events, you'll also have to determine which event occurred in the
- given window). This is probably the only time you'll need the window's number
- (i.e., to select the proper code to handle a window's events). Within your code
- to process events for a window, use CurWinPtr to index the WinParms array
- to get data for the event that occurred. CurWinPtr contains the handle of the
- current window with focus (which is the window that contained the event that
- caused control to be returned from WinEvent). Section 3.2 contains pseudo code
- that demonstrates this technique (called event-driven programming). WinNum is a
- cross reference array containing window numbers. WinNum(i) contains the window
- number that corresponds to handle i - see Section 5.14 for details on WinNum.
-
-
- 5.8 SaveText
- ------------
- This structure is DIMed as follows:
- SaveText(1 to MaxTextWins, 1 to MaxTextLines) AS STRING
-
- It is used to hold all scrollable text in all currently open windows. The
- first dimension defines "slots" for the set of scrollable text in a given
- window. The second dimension defines specific lines of text in that window.
- OpenScrollWindow takes the string array passed with scrollable text and copies
- it into an open slot in SaveText. It saves the "slot" value in
- WinParms(CurWinPtr,18) - see Section 5.9 for details on WinParms().
-
- Upon returning from WinEvent, if action=2 (i.e., text line selected), then
- SaveText(i,j) will be the line of text with focus; where:
- i=WinParms(CurWinPtr,18) and j=WinParms(CurWinPtr,15)
- SaveText is new with V2.0.
-
-
- 5.9 WinParms
- ------------
- This structure is DIMed as follows: WinParms(1 to MaxWindows, 1 to 19) The
- first dimension is called the window's "handle". This is the "slot" where data
- for a given window is saved when it's opened. CurWinPtr (see Section 5.6)
- always contains the handle of the window with focus.
-
- Each open window has the following 22 entries defined:
-
- 1 --- starting row of window (relative to screen)
- 2 --- starting column of window (relative to screen)
- 3 --- ending row of window (relative to screen)
- 4 --- ending column of window (relative to screen)
- 5 --- color attribute of window's background
- 6 --- color attribute of window's border
- 7 --- border type: (neg value will prevent scroll bar from being used)
- 1 = single line, 2 = double line
- -1 = single line, -2 = double line
- 8 --- color attribute of foreground text in window
- * 9 --- index of scrollable text's array entry currently at top of win
- 10 --- starting row of scrollable text area (relative to window)
- 11 --- starting column of scrollable text area (relative to window)
- 12 --- ending row of scrollable text area (relative to window)
- 13 --- ending column of scrollable text area (relative to window)
- * 14 --- absolute screen row of scrollable text line that currently has focus
- * 15 --- 2nd index in SaveText array: text line with focus (-1 if no text)
- * 16 --- handle of button with current focus (-1 if no button has focus)
- 17 --- last row of SaveText array with non-null value
- 18 --- 1st index in SaveText: all of window's text (-1 if no text)
- 19 --- mode of window (1=modeless; 2=modal; 3=immediate close; 4=wallpaper)
- (negative values imply window is shadowless)
- 20 --- # rows in original window (used for resize)
- 21 --- FLAGS
- 01h - movable (1=movable; 0=unmovable)
- 02h - sizable (1=sizable; 0=not sizable)
- * 04h - window minimized status (1=minimized; 0=not minimized).
- 22 --- # columns in original window (used for resize)
-
- Values marked with * are dynamic and will change as the active window is
- manipulated within the WinEvent function. All other values remain unchanged
- after window is opened (unless window is moved, in which case row/col values
- are translated based upon new location of window). If not used (i.e., no
- scrollable text, buttons, etc. in the window), then entries 9-22 are
- initialized to -1.
-
- Entries 17-21 were new with V2.0.
- Entries 20-22 were changed in V2.1.
-
- Examples: If WinEvent returns an action code of 3 (signifying a button
- selection event), then WinParms(CurWinPtr,16) contains the handle of the button
- that was clicked. If you code; han=WinParms(CurWinPtr,16) in the routine that
- processes a button click, then ButtonsData(han,i) contain information on the
- corresponding button (see Section 5.12) and ButtonsText(han) contain the actual
- text in the button (see Section 5.11).
-
- If WinEvent returns a value of 2 (scrollable text line selected), then
- SaveText(i,j) contains the line of text that was selected (i.e., has focus);
- where: i=WinParms(CurWinPtr,18) and j=WinParms(CurWinPtr,15)
-
- Note that if the unmovable flag is set, then the window also cannot be resized
- (regardless of the state of the sizable flag).
-
-
- 5.10 button handles
- -------------------
- When a push button, check box, input field or static text is created with
- LangWin's corresponding "make" or "show" routine, its data is stored in a
- "slot" of the ButtonsText and ButtonsData structures (described below). This
- "slot" is the index of the object's entry in these structures and is referred
- to as its "handle". The "make" routines return a handle value which should be
- saved in a unique variable name for later reference.
-
-
- 5.11 ButtonsText
- ----------------
- This structure is DIMed as follows: ButtonsText(1 to MaxButtons) AS STRING
-
- If Han is the handle returned by the corresponding "make" or "show" routines,
- then for each of the following objects, ButtonsText(Han) contains
- (see ButtonsData(i,8) in next section for button type):
-
- BUTTON TYPE CONTENTS OF ButtonsText(Han)
- ----------- ----------------------------
-
- Push Button: The push button's text (created with MakePushButton).
-
- Check Box: The symbol currently displayed in the check box (created with
- MakeCheckBox). By checking the state of the check box symbol's
- shadow (see Section 5.12), you can determine if the box was
- selected or not.
-
- Input Field: The current contents of the input field (created with
- MakeInputField). Input fields can either be created with initial
- text or a null string and can be modified by the user. By checking
- the contents of ButtonsText(handle) you can determine if the user
- made any changes (your code will have to "remember" the previous
- contents to determine if a change was made). If the input field is
- created (MakeInputField) with a negative value for length, then
- it can be used for password entry. All text entered will be
- displayed with the * character, but the actual password will be
- saved in ButtonsText(handle).
-
- Static Text: A line of static text placed into the window with the
- ShowWinText routine or a window title created with the
- ShowTitle routine. Each line of static text and each title
- occupies a separate entry in ButtonsText (new with V2.1).
-
-
- 5.12 ButtonsData
- ----------------
- This structure is DIMed as follows: ButtonsData(1 to MaxButtons, 1 to 10)
- All values are initialized to -1.
-
- Each push button, check box, input field, or static text has the following 8
- entries defined:
-
- 1 --- index to WinParms entry (i.e., the handle) of the window containing
- this button.
- (0 if this entry is unused).
- (<0 if this object is not visible,
- i.e., window was resized smaller and object no longer fits).
- 2 --- button's current absolute row (relative to entire screen).
- 3 --- button's current absolute column (relative to entire screen).
- 4 --- length of button (in characters).
- (<0 if button is visible but inactive; see DeactivateButton in 6.1)
- 5 --- foreground color attribute.
- 6 --- background color attribute.
- 7 --- current shadow state (0 = no shadow; 1 = shadow).
- [can be used to determine if check box is up (1) or down (0)]
- 8 --- button type: 1 = push button, 2 = input field, 3 = check box
- 4 = static text.
- 9 --- # rows in original window (used for resizing).
- 10 --- # cols in original window (used for resizing).
-
- For window titles, entry 8 will be a 4, and entries 2, 3, 4, 9, and 10 will be
- a -1.
-
- Entries 2, 3, and 7 are dynamic and can change as button is moved or
- (in the case of a check box) selected.
-
- Examples: If you defined a check box in the current window, and saved its
- handle value (returned by MakeCheckBox) in C1, then you can test the value of
- ButtonsData(C1,7) after WinEvent returns control (regardless of the event) to
- determine the state of the check box. If ButtonsData(C1,7)=0, then the check
- box has no shadow and it is down. If ButtonsData(C1,7)=1, then the check box
- has a shadow, and it is up.
-
-
- 5.13 UserHotKeys
- ----------------
- WinEvent sets 3 standard action codes: 1 = close; 2 = text line selected; 3 =
- button selected. The UserHotKeys data structure is used to define additional
- GLOBAL hot keys (i.e., available from ALL open windows) and corresponding
- action codes. Under most circumstances, UserHotKeys should be defined once
- BEFORE the main WinEvent loop.
-
- Each user defined hot key has the following 2 entries in UserHotKeys:
-
- 1 --- ASCII code (decimal) corresponding to the hot key. This is the code
- that is returned by INKEY$ when the key is pressed. For 2-byte key
- codes (for example Alt-z returns two bytes: 0 and 44), use the
- negative value of the second byte (which would be -44 in the above
- example). For 1-byte codes (i.e., Ctrl-z returns a 26, Shift-z
- returns a 90, and z returns a 122), use the positive value of the
- ASCII code.
-
- 2 --- WinEvent action code corresponding to the above key. WARNING:
- action code must NOT be 1, 2, or 3. These are reserved for WinEvent
- (if you choose action codes 1, 2, or 3, your hot key will not be
- recognized).
-
-
- LangWin initializes UserHotKeys with 0 entries as follows:
- REDIM UserHotKeys(0,1 to 2)
-
- If you want to define N specific GLOBAL hot keys, then you must first REDIM
- the UserHotKeys array for N entries (i.e., REDIM UserHotKeys(0 to N, 1 to 2),
- and then define each hot key and its corresponding WinEvent action code in the
- UserHotKeys array (the 0th entry is required, but MUST be left empty).
-
- For example, to define 3 hot keys (q, Ctrl-v, and Alt-m) with corresponding
- WinEvent action codes (4, 5, 6), the following code would be needed:
-
- REDIM UserHotKeys(0 TO 3, 1 TO 2)
- ' first hot key's definition
- UserHotKeys(1, 1) = 113 ' letter "q"
- UserHotKeys(1, 2) = 4 ' WinEvent action=4
- ' second hot key's definition
- UserHotKeys(2, 1) = 22 ' ctrl-v
- UserHotKeys(2, 2) = 5 ' WinEvent action=5
- ' third hot key's definition
- UserHotKeys(3, 1) = -50 ' alt-m
- UserHotKeys(3, 2) = 6 ' WinEvent action=6
-
- ____________________________________
- Notes: When REDIMing the UserHotKeys array, the lower bound of the first index
- MUST be 0. The 0th entry never contains hot key data, but it is required by
- LangWin. The upper bound of the first index must be the number of user defined
- hot keys required.
-
- User defined hot keys are active for ALL windows until the contents of
- UserHotKeys are changed or UserHotKeys is REDIMed to have 0 entries [i.e.,
- REDIM UserHotKeys(0, 1 to 2) ]. By changing the contents of UserHotKeys "on the
- fly" you can dynamically change the GLOBAL hot keys and/or their action codes,
- but this is NOT recommended (at best, it would produce an inconsistent
- interface to your users since hitting a given hot key would produce different
- results at different times).
-
- You should not choose return codes 1, 2, or 3 for any user hot keys.
-
- You cannot define any hot keys that are already used by WinEvent.
- The keys used by WinEvent are:
-
- Ctrl-M --- generates code 13 which is ENTER (selects button with focus)
- Ctrl-I --- generates code 9 which is TAB (gives focus to next object)
- Ctrl-[ --- generates code 27 which is ESC (close window event)
- Ctrl-a --- causes an "about" window to be displayed
- Ctrl-PgUp - generates a code -132 (gives focus to next window)
-
-
- 5.14 WinNum
- -----------
- This structure is DIMed as follows: WinNum(1 to MaxWindows). It is used as a
- cross reference between window handle and window number (see Section 5.7 for a
- discussion on the difference between these two values). WinNum(i) contains the
- window number for window handle i. WinNum is new with V2.0.
-
-
-
- --------------------
- 6.0 ADVANCED WINDOWS
- --------------------
- Well, if you made it this far in LangWin's user's guide, you are a true hacker
- (I use that term, as it was originally defined when computers had vacuum tubes,
- as a compliment for someone who truly wants to understand and use all of the
- intricacies of a system). I'll share a few hints, tips, and secrets for getting
- more miles/gallon from LangWin. Most of this section was re-written for
- LangWin 2.0; I encourage users of LangWin 1.x to read this section is detail.
- In addition, you should read the description of every routine in WINHELP.BAS
- (not every LangWIn routine is described in this user's guide).
-
-
- 6.1 Adding Frills to Your Windows
- ---------------------------------
- LangWin has a number of routines for adding frills to your windows. I'll cover
- some them briefly here. Load WINHELP.BAS into QB and hit F2 to see a detailed
- description of ALL routines (i.e., all of LangWin's routines are not described
- in the user's guide; WINHELP.BAS is the real reference guide for LangWin).
-
- In general, you can specify the position, color, and contents of various
- objects to be placed in the window . These routines should be called just after
- you create a given window and before you create another window or wait for
- events.
-
- ChangeButtonFocus - reverse video a button's text to show it has focus
- MakeBox - place a single/double line box in the window
- MakeHorizLine - draw a horizontal line across the window
- MakeVertLine - draw a vertical line across the window
- ShowTitle - place a title, centered, at top of the window
- ShowWinText - place some text in the window
-
-
- See SAMPLE0x.BAS, included on your LangWin distribution disk, for sample code
- that uses the above routines.
-
-
-
- 6.2 Window Modes, Shadows, and Movement
- ---------------------------------------
- With LangWin 2.0, there are four modes that can be assigned to a window
- when it is opened (see descriptions of BlankWin and OpenScrollWindow
- for calling parameters and values):
-
- Modeless: This is a "normal" window. When it has focus, you can click on
- any other visible window, and the new window will be given
- focus.
-
- Modal: When a modal window has focus (just after it is opened),
- clicking on any other visible window will NOT produce an event.
- This type of window can be used to display an error or warning
- message that you want the user to read and acknowledge (by
- either clicking on an "OK" button in the window, or by double
- clicking the close icon). Your code should then close the
- window. In most cases, the modal window ALWAYS retains focus
- until it has been closed. Clicking other windows will NOT give
- them focus. (An exception would occur if you placed a button in
- a modal window that caused another window to be opened. This new
- window could any mode - which might result in some interesting
- bugs, so I don't recommend opening any new windows while a
- modal window has focus. After any event occurs in a modal
- window, just close the window. The point is that while a modal
- window has focus, NO OTHER window can be given focus or
- generate an event.
-
- Immediate Close: When this window has focus, clicking on any other visible
- window will automatically generate a close action code (just
- as if the close icon was double clicked). The immediate close
- type window can be used to display pull down menus. If a pull
- down menu is visible, and the user clicks elsewhere, a close
- action code is returned and the pull down menu is closed
- (by your program's responding to the close action code). If you
- want your pull down menu to remain on the screen when the user
- clicks elsewhere, make it a modeless type.
-
- Wallpaper: This is a static or background only window. It can NEVER get
- focus, NEVER have any actions, and NEVER be moved. WinEvent
- recognizes when a wallpaper window is clicked and ignores the
- action. Wallpaper windows can be used to display static
- information or as background for other windows (see
- Section 6.3). You do not need to test for any events in a
- wallpaper window. If you need to reuse the screen space
- occupied by a wallpaper window, you will have to manually make
- it current and close it (since the user cannot directly cause a
- close event for that window).
-
- Wallpaper window mode can only be selected from the BlankWin
- routine (OpenScrollWindow cannot create a wallpaper window).
- It goes without saying that you should not create any objects
- (buttons, etc) in a wallpaper window (you can place static
- text in a wallpaper window). Since wallpaper windows cannot
- have any actions, you cannot create a wallpaper window with a
- close icon.
-
- In addition to the window's mode, the window can have three other attributes:
- shadow/shadowless, movable/unmovable, and sizable/not-sizable. These are self
- explanatory. The shadow feature is controlled by the sign of the mode parameter
- in BlankWin and OpenScrollWindow. If the mode parameter is negative, the window
- is shadowless. The movable feature is controlled by the sign of the window
- color parameter in BlankWin and OpenScrollWindow. If the window color is
- negative, then the window will be unmovable. The sizable feature is controlled
- by the sign of the border color parameter in BlankWin and OpenScrollWindow.
- A negative value will prevent the window from being resized (in addition, if
- the window is unmovable, it cannot be resized - regardless of the state of the
- sizable flag).
-
-
- 6.3 Using Wallpaper Windows
- ---------------------------
- Wallpaper windows can NEVER get focus and NEVER generate any actions. Two
- possible uses are: Information Only windows and background windows (to give
- the "illusion" of multiple scrollable lists in one window). The following
- sections describe these two techniques.
-
-
- 6.3.1 Information Only Windows
- ------------------------------
- You should use WinEvent to wait for an event from any open window. WinEvent
- will return the window's number and an action code. These will define where the
- event occurred (window number) and what object caused the event (action code).
- In general, after control returns from WinEvent, you must test for every
- possible window number that could be open by comparing the window number
- returned from WinEvent (which defines the window that had focus when the event
- occurred) to the set of unique window numbers that were returned by all of the
- BlankWIn or OpenScrollWindow routines used to create windows.
-
- If, however, you wish to create an "information only" window that can NEVER
- have any user actions (including closing it!), then you need not test for the
- information window's number in your WinEvent loop. In the simplest case, where
- you only want to place an info window on the screen and no other windows, then
- you don't even need a WinEvent loop. Making the Info Window's mode 4 will
- insure that no events can occur from the information window.
-
- Remember, the info window cannot be selected or closed by the user (you can
- close it with the CloseWindow routine as long as you manually make it current
- before calling CloseWindow - see CloseWindow in WINHELP.BAS for an example
- of how to do this). Also, remember that if you have info windows open, the
- AnyWinOpen global variable will remain TRUE. Thus, using "DO WHILE AnyWinOpen"
- to control your WinEvent loop will never exit because the info window will
- remain open (and AnyWinOpen will be TRUE) even after all other windows are
- closed. In this case, you'll have to explicitly break out of the loop (i.e.,
- EXIT DO) when you detect a close event (action=1) in your main window.
-
- One example would be to display instructions that need to remain on the
- screen for subsequent windows. Open a window and display the instructions (see
- ShowWinText). Then open another window (which becomes the active window) and
- loop through WinEvent waiting for events. If the instruction window is Mode 4,
- it cannot be moved by the user. It should be positioned so that subsequent
- windows do not overlay it (thus obscuring the information you wanted to
- display). Remember that after you close all subsequent windows, the instruction
- window again becomes the active window and should be closed.
-
-
- 6.3.2 Multiple Scrollable Lists In One Window
- ---------------------------------------------
- LangWin only supports one scrollable list per window. There are times when
- you'll want to provide the "illusion" of several scrollable lists in one
- window. Open a shadowed wallpaper window, then open several scrollable lists
- "on top" of the wallpaper window, using the same color as the wallpaper. Make
- these windows shadowless and unmovable. The user will "see" one window with
- multiple scrollable lists. Your program will be responding to several windows,
- all of which happen to be of the same color as the wallpaper window. Since the
- wallpaper window can NEVER be given focus (WinEvent makes sure of this), you
- don't have to worry that user will click the wallpaper, make it current, and
- cause it to "overlay" (i.e., hide) all other windows that were placed "over"
- it. SAMPLE04.BAS contains code that implements this technique.
-
- When you use the above technique to open several windows, giving the illusion
- that they all are really one window, you will want that window to appear to be
- modal. That is, all mouse clicks on windows other than the "multiple window"
- should be ignored until the multiple window is closed (otherwise portions of
- the multiple window would get overlaid, impacting the illusion that the
- multiple window is one entity). The "multiple window" is just that: several
- windows. How does one prevent mouse clicks from being accepted on windows
- other than the "multiple window", while allowing mouse clicks on any of the
- component windows that make up the multiple window? Good question! I'm glad
- you asked. First, you can't just make all component windows of the multiple
- window modal. This would result in only the last component of the multiple
- window accepting mouse clicks (if the last component window is modal, WinEvent
- will restrict mouse clicks that window).
-
- If there are no other windows open when the multiple window is created,
- there's no problem (because there's no other windows where the mouse could be
- clicked). However, if other windows exist when the multiple window is created,
- these existing windows must not be able to accept mouse clicks while the
- multiple window is open. In order to accomplish this, temporarily set all
- windows that exist prior to creating the multiple window to wallpaper mode. As
- wallpaper, WinEvent will ignore any events in these windows. The components of
- the multiple window can then be opened as modeless (allowing actions in any of
- the components).
-
- The following code shows this technique for one existing window whose number
- is saved in Main1:
-
- x=IsWinOpen(Main1,han) ' get Main1's handle
- ' assume you KNOW it's open, and no need to
- ' test return from IsWinOpen for TRUE.
- omode=WinParms(han,19) ' save main window's current mode
- WinParms(han,19)=4 ' set mode to wallpaper
-
- ' now open multiple windows and process events
- .
- .
- .
- ' done with multiple windows, close them all
-
- WinParms(han,19)=omode ' restore original mode
-
-
-
- 6.4 Modifying Scrollable Text While Its Window Is Open
- ------------------------------------------------------
- All scrollable text is saved in the SaveText array (see Section 5.8). One use
- for scrollable text is to present a list of items that can be selected for
- later action (e.g., a list of files to be printed, deleted, etc). The user
- would scroll through the list and select desired items by double clicking each
- one. After all items have been selected, an "action" button would be clicked to
- process all previously selected items.
-
- In order to implement this function, you must first give the user feedback that
- the text line clicked has actually been selected. One possible way to do this
- is to include specific characters in the scrollable text that can be used to
- denote selection or un-selection. For example, every line of scrollable text
- could begin with [ ] (that is, left bracket, space, right bracket) if it is not
- selected, and [X] if it is selected.
-
- When a line of text is double clicked, it is given focus and control is
- returned from WinEvent. The value returned from WinEvent is the window's
- number. The value returned in the action code will be a 2. Your code would
- examine the window's number and action code, and give control to the proper
- code segment (see Section 3.2). At that point, CurWinPtr (a global variable,
- see Section 5.6) will contain the current window's handle. SaveText(i,j) will
- contain the scrollable text with focus [where: i=WinParms(CurWinPtr,18) and
- j=WinParms(CurWinPtr,15)].
-
- To denote selection of the text line in focus, your program would first have to
- change the second character in SaveText(i,j) to an "X". The text line would
- then begin with [X] to indicate selection. Actually, you would first want to
- test the contents of the selected text line's second character, so you could
- toggle a space to an X, and an X to a space (thus, the user could click on the
- text and toggle the selection on/off).
-
- Once you've changed the scrollable text's selection character (on or off),
- you'll need to actually update the contents of the visible window. To do this,
- you must re-write that line of text into the proper position of the window. The
- ReShowText routine will accomplish the necessary refresh into the current
- window. After you refresh the window, continue looping through WinEvent.
-
- When the user finally selects the "action" button, WinEvent will return an
- action code of 3. After determining which window and which button was selected,
- you can take appropriate action on all text lines selected. Merely step through
- every entry in the current window's scrollable text, examine the second
- character, and if it's an X, take action. The current window's scrollable text
- can be found in SaveText(i,x) where: i=WinParms(CurWinPtr,18) and x = 1 to
- WinParms(CurWinPtr,17).
-
- In addition, you should re-set the selection, by changing the X back to a blank
- in the text array. Again, you must update the contents of the window to show
- that all previously selected items have now been un-selected. The ReShowPage
- routine will accomplish this task (ReShowText CANNOT be used since it ONLY
- refreshes the current line of text that has focus, and you may need to refresh
- many text lines). After you refresh the window, continue looping through
- WinEvent.
-
- SAMPLE02.BAS has sample code that implements the above technique.
-
-
- 6.5 Changing the Entire Scrollable Text Array While its Window is Open
- ----------------------------------------------------------------------
- The previous section described how you can change individual lines of
- scrollable text in an open window. It is also possible to replace an open
- window's scrollable text with an entirely new set. The new set can either have
- more, less, or the same number of lines. For example, if your window displays a
- directory structure and contents, and the user changes directories, you may
- want to show a completely different list of file names in the window.
-
- The RefreshScrollText routine (new with V2.0) can be used to replace the
- scrollable text in the current window with focus. If the window to be updated
- is not current, you must manually make it current (see RefreshScrollText in
- WINHELP.BAS for an example of how to do this). You must place the new
- scrollable text into a string array whose LBOUND is 1. This string array is
- used as a parameter to the RefreshScrollText routine (see WINHELP.BAS for
- additional details). Run time errors will occur if the current window with
- focus has no scrollable text or LBOUND of the string array is not 1. After
- control is returned from RefreshScrollText, I recommend that you ERASE the
- string array (passed as a parameter to RefreshScrollText) in order to save
- memory. If the UBOUND of your string array is greater than MaxTextLines, then
- only the first MaxTextLines of the array will be displayed, and the
- MaxTextLines entry will appear as: (Incomplete List) to let your user know
- that all data is not visible. See Section 5.3 for additional details on
- programming to handle this case.
-
-
-
- SAMPLE04.BAS contains examples of techniques to modify the contents of
- scrollable arrays while a window is open.
-
-
- 6.6 Modifying the Contents of an Input Field While Its Window Is Open
- ---------------------------------------------------------------------
- Similar to the technique for modifying scrollable text (see Section 6.4), you
- might need to modify the contents of an input field based upon other events in
- the window (for example, the WINCOLOR routine, included on the LangWin
- distribution disk, has input fields that can be updated directly or
- incremented/decremented by selecting a push button). When a push button is
- selected, WINCOLOR updates the corresponding input field (i.e., increments or
- decrements the current value and redisplays the updated value).
-
- Your program's logic will have to determine the handle of the input field that
- should be updated. This can be done in response to a specific event in the
- window (i.e., when a specific button is clicked, you may need to update a
- specific input field). Let's call that handle: Han. To modify the corresponding
- input field's contents, just change the appropriate characters in
- ButtonsText(Han). Remember that ButtonsText is a STRING array. If the contents
- represent numeric data, use the VAR command to convert to numeric, modify, and
- use STR$ to place the equivalent string values back into ButtonsText(Han).
-
- After the ButtonsText array has been updated, the window must be refreshed. Use
- ReShowInputField to accomplish this task (similar to ReShowText in Section
- 6.4). After the input field has been updated and redisplayed, continue with the
- WinEvent loop.
-
-
- 6.7 Determining What Had Focus When WinEvent Returns Control
- ------------------------------------------------------------
- When control is returned from WinEvent, you will need to determine two things:
- which window had focus and what action occurred in that window. The following
- sub-sections address these two event-driven programming tasks.
-
- 6.7.1 Determining Which Window Had Focus
- ----------------------------------------
- As described in Section 5.7, the WinEvent function returns a value that
- corresponds to the number of the window that had focus when an action occurred.
- When every window is created (by either BlankWin or OpenScrollWindow), it is
- assigned a unique window number (value returned by the function). You should
- save these window numbers in unique variables (if the value returned is < 0,
- then an error has occurred when creating the window, see Section 6.14 for more
- details on handling errors). When control is returned from WinEvent, you need
- to compare the window number returned (by WinEvent) to the set of all window
- numbers saved when your windows were created. When you find a match, you know
- what window had focus (and you can then determine which action occurred in that
- window - see Section 6.7.2).
-
- The easiest way to implement the above technique is with a SELECT CASE
- structure. Assume that three windows have been created, and their numbers are
- saved in variables: w1, w2, and w3. The following pseudo code will determine
- which window had focus when an event occurs:
-
- ' open some windows
- w1=BlankWin(parms)
- w2=OpenScrollWindow(parms)
- w3=BlankWin(parms)
-
- do while AnyWinOpen ' loop as long as windows are open
- ' wait for an event, window number returned in variable: wnum
- wnum=WinEvent(action)
- select case wnum ' determine which window had focus
- case w1
- ' process actions for window: w1
- case w2
- ' process actions for window: w2
- case w3
- ' process actions for window: w3
- end select
- loop
-
-
- The above code handles static windows: w1, w2, and w3, that is windows
- specifically opened in your code before waiting for events. Your WinEvent loop
- must test for every static window that was opened. However, the code that
- processes events for a specific window might also open windows dynamically,
- that is under control of your end user and the events that are selected at run
- time. Thus, your WinEvent loop must not only test for all possible static
- windows, but also all possible dynamic windows.
-
- For example, in the above code, assume that in window w2, a new window should
- be opened (number saved in w2a) when a specific event occurs (a button click).
- Then, your loop must also include a CASE statement for w2a (to handle any
- events that could occur if/when window w2a is open and current). The code new
- might look like:
-
- ' open some windows
- w1=BlankWin(parms)
- w2=OpenScrollWindow(parms)
- w3=BlankWin(parms)
-
- do while AnyWinOpen ' loop as long as windows are open
- wnum=WinEvent(action) ' wait for an event
- select case wnum ' determine which window had focus
- case w1
- ' process actions for window: w1
- case w2
- ' process actions for window: w2
- .
- .
- .
- if specific button pressed then
- w2a=BlankWin(parms)'open a dynamic window under user control
- end if
- .
- .
- .
- case w3
- ' process actions for window: w3
- case w2a
- ' process actions for window :w2a
- end select
- loop
-
- Thus, as you begin developing code segments to handle every possible event, you
- will find yourself adding additional segments to handle dynamic windows that
- can be open due to actions or errors.
-
- 6.7.2 Determining What Action Occurred In The Window
- ---------------------------------------------------
- WinEvent takes one parameter. Upon return, this parameter is set to an action
- code: 1=close; 2=scrollable text; 3=button.
-
- Typically, the code segment for each window will have to test the action code
- (returned by WinEvent) and take appropriate action. Again, the SELECT CASE
- structure can be used. The following pseudo code would reside within the
- segment for a given window (window w2 is used in the example):
-
- .
- .
- do while AnyWinOpen ' loop as long as long as windows are open
- wnum=WinEvent(action) ' wait for an event
- select case wnum ' determine which window had focus
-
- .
- .
- case w2
- ' process actions for window w2
- select case action
- case 1
- ' process the close action in window w2
- case 2
- ' process the selected scrollable text in window w2
- case 3
- ' process the button clicked in window w2
- end select
- .
- .
- end select
- loop
-
-
-
- If the action code was 2, then a line of scrollable text was selected (i.e., it
- has focus). The selected line of text can be found in SaveText(i,j); where:
- i=WinParms(CurWinPtr,18) and j=WinParms(CurWinPtr,15). In this case, no button
- could have had focus (i.e., when a line of text is clicked, focus is removed
- from any button). Process SaveText(i,j) as necessary (see Section 6.4 for an
- example).
-
- If the action code was 3, then a button was clicked. Your code segment must
- determine which button has focus (if there were more than one buttons in the
- window). WinParms(CurWinPtr,16) contains the handle of the button with focus.
- Use this value in a SELECT CASE to process every possible button created in the
- given window. Button handle values are returned by the MakePushButton routine
- and should be saved in unique variable names. These variables will then be used
- in your CASE statements. If a button was clicked, it is given focus and control
- returned from WinEvent. In this case, scrollable text could also have focus (if
- scrollable text exists). Your code segment will have to determine if any
- actions should be taken with any scrollable text that also has focus.
-
- The following code segment shows how you might process a close action, clicking
- on scrollable text, or clicking on a button in a given window (w2). If button
- w2b2 is clicked, a dynamic window is opened.
-
- .
- .
- w2=OpenScrollWindow(parms) ' open a window
- w2b1=MakePushButton(parms) ' create buttons in
- w2b2=MakePushButton(parms) ' the window
- .
- .
- do while AnyWinOpen ' loop as long as windows are open
- wnum=WinEvent(action) ' wait for an event
- select case wnum ' determine which window has focus
- .
- .
- .
- case w2 ' process events in window: w2
- select case action ' determine which action occurred
- case 1
- ' process the close action in window w2
- xx=CloseWindow ' close the window
- case 2
- ' process the selected scrollable text in window w2
- slot=WinParms(CurWinPtr,18) ' slot with window's text set
- row=WinParms(CurWinPtr,15) ' row of text with focus
- process SaveText(slot,row)
- case 3
- ' process the button clicked in window w2
- select case WinParms(CurWinPtr,16) 'determine which button
- case w2b1
- ' process a click on button: w2b1
- case w2b2
- ' process a click on button: w2b2
- ' open a new window if button w2b2 is clicked
- w5=OpenScrollWindow(parms)
- end select
- end select
- .
- .
- end select
- loop
-
-
- Once you understand the above technique, refer to SAMPLE0x.BAS, included on the
- LangWin distribution disk, for complete code that implements windows. If you
- need to reference LangWin's routines and their actual parameters, load
- WINHELP.BAS (which was included on LangWin's distribution disk) into QB and hit
- F2.
-
-
- 6.8 Nesting Calls to WinEvent
- -----------------------------
- As you can see from the previous code examples, things can get complex very
- quickly. Your WinEvent loop must have a CASE for every possible window (both
- static and dynamic). Within each code segment that processes a window, you need
- CASE statements for every possible action code (at least codes 1-3 plus any
- codes for user defined hot keys). Then, within the code segment to handle
- action 3 (button click), you need a CASE statement for every button created in
- that window. For a complex set of windows, with several menus, sub-menus,
- buttons, user hot keys, and dynamic error windows, your code will be long.
-
- One way to make your code more readable is to perhaps limit the main module to
- just the CASE statements for all windows. Once you determine which window had
- focus, you could call a separate subroutine to process each window. The
- subroutine would have to be aware of the button handles created for its window
- in the main program, and if the subroutine opens any windows and saves their
- handles in variables, the main routine must have access to these variables
- (because the main module needs a CASE statement for every possible window). A
- good exercise in sharing variables.
-
- The subroutine that processes events for a given window could create child
- windows and have its own WinEvent loop. In this case, that WinEvent loop would
- only be "aware" of events in windows it was programmed to respond to (i.e.,
- child windows opened while control was in the subroutine). Clicking other
- windows and objects would have no effect until the subroutine returned control
- back to the main module. In some circumstances, this technique might not only
- make your code simpler to understand/debug, but may be exactly the method you
- need to process child windows. Care should be taken in nesting calls to
- WinEvent (from within a WinEvent loop, calling a subroutine that has its own
- call to WinEvent and loop, which in-turn calls a subroutine with a WinEvent
- loop, etc.). I've had stack overflows when trying to nest too many calls to
- WinEvent. (What "too many" is depends upon memory availability. I'd recommend
- that you don't nest more than 3 or 4, after that your code is probably far too
- complex to deal with anyway).
-
-
- 6.9 Deactivating and Activating Buttons
- ---------------------------------------
- The pseudo code in the previous sections demonstrated how you might open a
- dynamic window when a specific event occurred (i.e., a button click).
- Typically, when this new window has been opened, you may want to prevent the
- end user from continuing to click the same button and opening more windows of
- the same kind (this could quickly cause MaxWindows, maximum number of windows
- that can be open, to be exceeded and perhaps your program to fail). One way to
- prevent additional windows from being opened when the same button is clicked is
- to make the window's mode either Modal or Immediate Close (see Section 6.3 for
- details). In the case of a Modal window, the user MUST close the window before
- any other event can take place (thus clicking the same button again is not
- possible until the dynamic window is first closed). In the case of an Immediate
- Close window, clicking the same button will first result in a close action for
- the dynamic window (i.e., clicking anywhere off the Immediate Close window
- will cause it to be closed).
-
- Both of the above techniques (Modal and Immediate Close) have their
- disadvantages. In particular, you may want to allow the user to mouse elsewhere
- (while the new dynamic window is open) and select other objects (except the
- button that originally caused the dynamic window to be opened). You may also
- want the new dynamic window to remain open while the user mouses elsewhere.
-
- To get around these disadvantages, you can use the DeactivateButton routine.
- This will clear the button's text and make it inactive. While the button is
- inactive, no action on that button will be recognized by WinEvent (i.e.,
- clicking the button will not cause a return from WinEvent). When you are ready
- to accept actions from the given button again (i.e., after the new dynamic
- window has been closed), then call the ActivateButton routine. See WINHELP.BAS
- for details on how to call these routines.
-
-
- 6.10 Giving a Specific Button Focus When It Is Created
- ------------------------------------------------------
- When buttons or check boxes are created (with the corresponding "make"
- routine), they are not given focus. Hitting TAB or an arrow key will cause the
- "next" object to get focus (and be displayed in reverse video). Hitting ENTER
- will then cause control to be returned from WinEvent with an action code of 3.
- WinParms(CurWinPtr,16) will contain the handle of the button in focus. Clicking
- on the button will also cause these events to occur.
-
- As you create buttons in a window, you might want to give one of them
- focus (i.e., make it the default choice). Your user could then click this
- button to select it, or just hit ENTER (since it will already have focus).
- The following code should be used to give a specific button focus (assume
- that the button's handle, returned from the corresponding "make" routine,
- is saved in Han):
-
- WinParms(CurWinPtr,16)=Han ' place handle in data structure
- CALL ChangeButtonFocus(Han,0) ' give button visual focus
-
- The above code MUST be placed AFTER the corresponding window has been created
- (with BlankWin or OpenScrollWindow), AFTER the button has been created (with
- a "make" routine), and BEFORE the next window is created (i.e., CurWinPtr
- must point to the current window where the button will be given focus - see
- Section 5.6).
-
- Only ONE button (or check box) per window can be given focus.
- WinParms(CurWinPtr,16) contains its handle. You could call ChangeButtonFocus
- multiple times to reverse video many buttons, but only the button whose handle
- is found in WinParms(CurWinPtr,16) will be recognized by WinEvent when
- processing mouse or keyboard input. (Calling ChangeButtonFocus for more than
- one button will only confuse your user).
-
-
- 6.11 Is A Given Window Open
- ---------------------------
- On occasion, you may need to determine if a specific window is open. For
- example, if you receive a click on an EXIT button, you may want to determine if
- any child windows have been opened dynamically (and close them first). Of
- course, you could have deactivated the EXIT button (see Section 6.8) when a
- child window is opened (which will prevent EXIT from being clicked until you
- activate the button when the child window is closed). However, let's assume
- that you will allow the user to click the EXIT button and automatically close
- any dynamically opened child windows along with the main window.
-
- The IsWinOpen function (new with 2.0) can be used for this purpose. Its input
- parameter is the window's number (NOT the window handle). It returns a TRUE
- value if the window is open; else it returns a FALSE value. IsWinOpen also
- takes a second (output) parameter. If the window number passed is open, the
- second parameter is set to the window's handle (thus IsWinOpen will also
- provide a window number to window handle cross reference function - also see
- Section 5.14).
-
- See WINHELP.BAS for additional details on IsWinOpen.
-
- 6.12 Manually Giving a Window Focus (i.e., via program control)
- ---------------------------------------------------------------
- When WinEvent has control, it will detect when the mouse is clicked on a window
- and (if appropriate) give that window focus (so that any subsequent objects
- selected in that window will cause control to be returned). The global variable
- CurWinPtr contains the handle of the window with focus.
-
- There may be occasions when your program will need to cause a specific window
- to obtain focus. The NewFocusWindow routine takes a window's handle as its
- parameter, and will cause that window to be given focus.
-
- For example, if a main window has an EXIT button, and the button is selected,
- then the main window should be closed. However, before the main window is
- closed, all open child windows should probably be closed (of course there are
- exceptions to this "rule"). The IsWinOpen (see previous section) can be used to
- determine if a specific child window is open (given the child window's number),
- and if so it's handle will be returned. Using that handle of each open child
- window, NewFocusWindow can be used to first give it focus, then close it (the
- CloseWindow routine closes the current window, so each child must first be made
- current before it can be closed).
-
- WARNING: NewFocusWindow will cause your program to terminate if an invalid
- handle is passed to it. The handle must correspond to an open window. One way
- to insure that you have a valid handle is to use the IsWinOpen routine which
- will return the handle of an open window (given that the window is open and you
- pass IsWinOpen it's window number).
-
- 6.13 State of Check Boxes
- -------------------------
- Clicking on a check box will change its state, but will NOT return an event
- from WinEvent. However, when an event does occur and control is returned from
- WinEvent, you may want to check the state of any check boxes in the current
- window. If you defined a check box in the current window, and saved its handle
- value (returned by MakeCheckBox) in C1, then you can test the value of
- ButtonsData(C1,7) after WinEvent returns control (regardless of the event) to
- determine the state of the check box. If ButtonsData(C1,7)=0, then the check
- box has no shadow and it is down. If ButtonsData(C1,7)=1, then the check box
- has a shadow, and it is up.
-
-
-
- 6.14 Run Time Errors
- -----------------------
- Many of LangWin's functions return codes indicating success or failure. For
- those routines whose parameters are static (not changable based upon user input
- at run time, but constants hard-coded in the parameter lists), you'll find most
- errors during development. For example, if a window fails to open, you can
- insert a breakpoint, check the return code, and figure out the error (i.e.,
- window won't fit on the screen, etc.). You could also place instructions after
- the CALL to these functions, test the return code, and print it if an error
- value is indicated (see WINHELP.BAS for a description of every routine and its
- return codes). I'd recommend this technique (i.e., testing return codes for
- error values) in all cases where the parameters to LangWin's functions are
- dynamic (i.e., based upon conditions that can be changed during run time).
-
- If a window fails to open (for example, because it will not fit on the screen),
- and you don't test for this error, then subsequent references to LangWin's data
- structures using CurWinPtr will reference the last window with focus. This will
- either result in a run time error (subscript out of range) or unpredictable
- results in your program.
-
- For example, with V2.1 resizing is supported. In addition to saving objects
- (i.e., buttons, check boxes, input fields) in data structures, window titles
- and static text must also be saved (new with V2.1). The global parameter
- MaxButtons determines the size of these data structures. If MaxButtons is too
- small, then the routines that create objects, titles, and static text will
- return an error code and the object will not be created. If you don't test for
- this error, you'll have windows that will be missing some/all expected objects
- (i.e., you could end up with an empty window). While this won't cause a fatal
- error, your windows might not contain the information you expect or need. All
- of the windows/objects created prior to entering the WinEvent loop can be
- visually examined to determine if all objects are present. However, if within
- the WinEvent loop you open child windows based upon the user's dynamic input
- (i.e., clicking a button), then you might need to visually examine a large
- number of combinations to insure that every possible set of windows that could
- be created on the screen has the correct objects (i.e., that MaxButtons is
- large enough). Thus, while it is not too important to check error codes prior
- to the WinEvent loop (because all windows and objects are statically created
- and can be checked visually), within the WinEvent loop I'd recommend more
- careful error checking.
-
-
- In general, I always check for error conditions when a window is opened (i.e.,
- BlankWin or OpenScrollWindow). When I call any other routine with dynamic
- parameters determined at run time (usually within the WinEvent loop), I also
- check for error codes.
-
-
- LangWin will generate a run-time error and abnormally terminate if any of the
- following conditions occur (routine names generating the error are given in
- parentheses):
-
- * MaxTextWins is greater than MaxWindows (LangWinInit)
- * LBOUND of string array passed as a parameter is not 1 (OpenScrollWindow
- and RefreshScrollText)
- * Current window with focus has no scrollable text (RefreshScrollText)
- * Invalid window handle passed as a parameter (NewFocusWindow)
-
-
- All of the above conditions can be avoided by careful programming (unless, of
- course, Murphy is lurking nearby!). Refer to the corresponding member in
- WINHELP.BAS for more details on these errors.
-
-
- 6.15 Testing for Color, B/W, EGA, or VGA
- ----------------------------------------
- LangWin requires a color monitor and EGA or better graphics. Here's a technique
- for determining if your user is running on a system with a color or black and
- white monitor:
-
- def seg = 0 ' low memory
- if peek(&h463) = &hB4 then mon$="b/w"
- if peek(&h463) = &hD4 then mon$="color"
- def seg
-
- Determining whether EGA or better graphics exists is less straight forward. One
- technique is to use ON ERROR and to vary SCREEN modes (i.e., 12, 9). Depending
- on which mode (if any) causes an error, you can determine which color graphics
- support exists. I don't really like this technique. It's "brute force", but it
- works.
-
- Another technique is to use BIOS interrupt 10h, functions 1Ah and 10h. I've
- had success with this technique, but my reference manuals say function 1Ah is
- for PS/2s. I've tried it on a non-PS/2 and it worked ok, but I can't say that
- it will work on every non-PS/2. To use this technique, first use the above
- test to determine if the monitor is color or b/w. Then call BIOS interrupt 10h
- function 1Ah. If the AX register is set to 1Ah upon return from the interrupt,
- then the function is supported by your BIOS. In that case, test the value of
- BX (4=EGA color, 5=EGA b/w, 7=VGA b/w, 8=VGA color). If AX is not set to 1Ah,
- then call BIOS interrupt 10h, function 12h, sub-function 10h. If BX is
- returned with a value of 1, then you have a b/w monitor. Else, if BX is not
- 10h, then you have EGA color.
-
-
- 6.16 Colors, Attributes, and Palettes
- -------------------------------------
- Throughout ALL of your code, you MUST use the SetColor routine instead of the
- COLOR command to change color attributes. The parameters are the same
- (foreground and background attributes) as the COLOR command. LangWin uses BIOS
- interrupt 10h, function 10h, sub-function 03h to disable blinking colors. This
- allows 16 attribute numbers for window (background) colors (rather than just
- 8). However, with blinking disabled, foreground and background attribute
- numbers MUST be translated before calling BASIC's COLOR command. SetColor does
- this translation and issues the COLOR command with the proper values. If
- BASIC's COLOR command is called without this translation, you will get
- unexpected colors.
-
- While in the QB development environment, if you interrupt your program under
- development, and use F4 to see the data displayed on the screen, you'll see
- blinking colors for background attribute numbers > 8. This is ok. As previously
- mentioned, LangWin disables the blinking bit and uses blinking background
- attributes (9-15) for additional colors. The QB environment, however, does not
- disable the blinking bit. So, when you hit F4 to see the screen, background
- attribute colors > 8 will blink. If you hit F5 to resume, the colors will still
- blink (because the blink bit is only disabled once in LangWinInit). If you
- restart your program (Shift-F4), then LangWinInit will again disable the
- blinking bit and colors will appear without blinking.
-
- LangWin supports 16 attributes (0-15) for colors. In text mode, however, you
- can assign up to 64 color values (0-63) to any given attribute number via the
- PALETTE command (i.e., PALETTE 5,27 assigns color number 27 to attribute 5).
- I've included a program called WINCOLOR on the distribution disk that will
- allow you to see all possible color values for various window entities. Once
- you find a set of colors values (as opposed to color attributes) that look
- good, then use PALETTE in your program to assign the color values to the
- attribute numbers coded in CALLs to LangWin routines.
-
- For example, MakePushButton requires two attributes as parameters (foreground
- and background of button). Assume you use attributes 2 and 3 for these
- parameters. Using WINCOLOR, you might determine that color values 23 and 41
- make pleasing foreground/background colors for your button (I just picked those
- two numbers at random and have no idea how they actually look). Using this
- example, you would code: PALETTE 2,23 (to set attribute 2 to color 23) and
- PALETTE 3,41 (to set attribute 3 to color 41). You would NOT need to modify
- your parameter list for MakePushButton (which would contain the attribute
- numbers 2 and 3).
-
- SAMPLE02.BAS has code that illustrates this technique.
-
-
- 6.17 WaitTicks
- --------------
- QuickBASIC has a command (SLEEP) that will allow your code to pause a given
- number of seconds. LangWin includes a routine (WaitTicks) that will allow your
- program to pause a given number of timer ticks (one timer tick is approximately
- 1/18.2 seconds). If you need finer granularity than seconds in a delay loop,
- use WaitTicks.
-
-
- 6.18 Video Pages
- ----------------
- If your video board has enough memory, you can display multiple video pages.
- Refer to your BASIC documentation on the SCREEN command. With V2.3, LangWin
- will support windows in any video page that is available on your system;
- however, you MUST only use one video page consistently for all of LangWin's
- displays. That is, you should not switch video pages in the middle of a
- program that uses LangWin to display windows. This could cause unpredictable
- results. You MUST also define the video page via a SCREEN command BEFORE
- the call to LangWinInit.
-
- LangWin keeps track of the coordinates of every object (button, check box,
- input field, etc.), but it does not remember the video page for these objects.
- LangWin assumes that all objects are in the same video page (I could add a
- feature to remember individual video pages for all objects, but LangWin's data
- structures are already getting too large). During initialization, LangWinInit
- obtains the current video page and saves this information in a global
- variable. All routines that read/write data directly in the video buffer use
- this global variable to place the data in the correct page. These routines
- will continue to write into the original video page regardless of the change
- in page number via the SCREEN command. However, some of LangWin's routines use
- the standard PRINT command to display data. These routines will display their
- data in the new video page. So, some data will be visible in the new page and
- some won't. Like I said, unpredictable results.
-
- In addition, if you change video pages with the SCREEN command, and happen to
- click on the coordinates of an object in the original page (which will be
- invisible at this point), LangWin will recognize the object and return control
- from WinEvent. Hitting enter will cause similar results with whatever object
- had focus (which might be on an invisible page). In these cases, your code
- will get control from WinEvent and process the event, even though the actual
- button might have been on an invisible page. Again, you could get
- unpredictable results. So, my advice is not to change video pages once the
- page number has been initially defined.
-
- The page number must be initially defined via a SCREEN 0,,x,x command (where x
- is a valid video page supported by your system). With V2.3, this command MUST
- be placed BEFORE the call to LangWinInit, which detects the video page and
- saves it in a global variable (actually the offset in the video buffer is
- saved in a global variable). See Section 3.1 for sample initialization code.
-
- There is one exception where changing video pages might be useful. If you plan
- to display data without the use of LangWin's routines, then you could change
- pages, display the data, then (when the user is finished with the data),
- change back to the page containing LangWin's display. For example, suppose you
- use LangWin to display a window with a scrollable list of file names. When the
- user clicks on a file name, you may want to display the file. One technique
- would be to change page numbers, use the SHELL command to exit to DOS and call
- a viewer to display the selected file. When the user exits the viewer, control
- would be returned from DOS back to your program at the point following the
- SHELL command. You would then reset the video page back to the original value
- and the scrollable list of files would again be visible. Before SHELLing to
- DOS to call the viewer, you should check for a mouse and hide the cursor. Upon
- return, check again and show the cursor. Here's a prototype of this example:
-
-
- ' prior to this code, you would have detected a scrollable text click
- ' (action=2) in the appropriate window.
-
- i=WinParms(CurWinPtr,18) ' index of clicked text block
- j=WinParms(CurWinPtr,15) ' specific index of text line
- filespec$=SaveText(i,j) ' text that was clicked
-
- IF HaveMouse THEN CALL HideMouseCursor ' hide mouse
- SCREEN 0,,x,x ' set to new page (you must define x)
- SHELL "view "+filespec$ ' call viewer (assume its in your PATH)
- SCREEN 0,,z,z ' z is the original page number
- IF HaveMouse THEN CALL ShowMouseCursor ' show mouse
-
- The above code is only a prototype. It obviously depends upon exactly what
- your viewer does. It's meant to give you an example of how you might use
- multiple video pages with LangWin.
-
- As an alternative to changing pages when shelling to an external program, you
- could open a window that covered the full screen. This would cause the entire
- screen's contents to be saved. Upon return from the external program, just
- close the current window. At that point, whatever the external program left on
- the screen would be visible; however, closing the current window would force
- LangWin to restore the contents of the screen "underneath" the last window
- opened. Since the last window opened covered the full screen, the effect of
- closing it would be to restore the entire screen (containing all windows that
- existed prior to shelling to the external program). The following code
- implements this technique for the file viewer example:
-
- ' prior to this code, you would have detected a scrollable text click
- ' (action=2) in the appropriate window.
-
- i=WinParms(CurWinPtr,18) ' index of clicked text block
- j=WinParms(CurWinPtr,15) ' specific index of text line
- filespec$=SaveText(i,j) ' text that was clicked
-
- IF HaveMouse THEN CALL HideMouseCursor ' hide mouse
-
- xx=BlankWin(1,1,MaxRows,MaxCols,0,0,1,0,0,-1)
- ' the above code will open a window that covers the entire screen.
- ' the window's color will be 0 (black).
- ' MaxRows and MaxCols are global variables defined by your initial
- ' call to LangWinInit.
- ' the effect will look like the screen has been cleared.
- ' the purpose of this call to BlankWin is to cause the contents
- ' of the screen "under" the window to be saved. that is, the
- ' entire screen will be saved. when this window is subsequently
- ' closed, the screen's contents will be restored.
-
- SHELL "view "+filespec$ ' call viewer (assume its in your PATH)
- x=CloseWindow ' close full screen window which will
- ' restore the screen
- IF HaveMouse THEN CALL ShowMouseCursor ' show mouse
-
-
- 6.19 Using Non-Text Mode Graphics With LangWin
- ----------------------------------------------
- It is possible to "jump" into non-text mode graphics (i.e., SCREEN 9,
- SCREEN 13, etc.) then back to text mode (i.e., SCREEN 0). However, be aware
- that when you jump to graphics mode, the screen will be cleared. I'll show you
- a technique for restoring the screen once you return to text mode, but when
- you jump from text to graphics, the contents of the screen (i.e., all windows
- and objects that have been displayed) will be cleared (but not lost). When you
- jump back from graphics mode to text mode, the contents of the graphics screen
- will be cleared and lost (unless you manually save the screen yourself).
- Finally, remember that LangWin's routines cannot be used while in graphics
- modes.
-
- Assume that you have opened some windows and placed objects in those windows.
- Also assume that you are in the loop that calls WinEvent and tests for the
- window number and action code. Depending upon a specific action (say a button
- click), assume you need to go into SCREEN mode 9, display some graphics, get
- user input, and then return to text mode. When you return to text mode, you
- want to see all windows and objects that existed before going to graphics
- mode. The following prototype code can be used:
-
- ' you determine that graphics mode is needed
-
- xx=BlankWin(1,1,MaxRows,MaxCols,0,0,1,0,0,-1)
- ' the above code will open a window that covers the entire screen.
- ' the window's color will be 0 (black).
- ' MaxRows and MaxCols are global variables defined by your initial
- ' call to LangWinInit.
- ' the effect will look like the screen has been cleared.
- ' the purpose of this call to BlankWin is to cause the contents
- ' of the screen "under" the window to be saved. that is, the
- ' entire screen will be saved. when this window is subsequently
- ' closed, the screen's contents will be restored.
-
- SCREEN 9 ' jump to graphics mode (this will actually cause the
- ' the screen to be cleared. since it's already black,
- ' clearing a second time won't be noticed).
-
- ' do your thing in graphics mode
-
- SCREEN 0 ' back to text mode. contents of graphics screen will be
- ' cleared
-
- WIDTH MaxCols, MaxRows ' reset rows/cols
- ' MaxRows and MaxCols are global variables defined by your initial
- ' call to LangWinInit.
-
- CALL BlinkOff
- ' the BlinkOff subroutine is included in LangWin. its purpose is to
- ' tell the BIOS to turn off the blink bit in the color attribute
- ' (see Section 6.16 for additional information). if you don't
- ' call BlinkOff here, and you've used any background color attributes
- ' greater than 7, then these colors will blink when your windows
- ' are restored
-
- CALL SetTextCursor(DefaultSmask, DefaultCmask)
- ' the above code will set the mouse text mode cursor to a white
- ' arrow on a black background. DefaultSmask and DefaultCmask are
- ' global variables defined by your initial call to LangWinInit.
-
- CALL ShowMouseCursor ' re-enable the mouse cursor
-
- xx=CloseWindow
- ' the above code will close the window that covered the screen.
- ' this will restore the screen's contents.
- ' your windows and objects should appear just as they did before
- ' jumping to graphics mode
-
-
- 6.20 Closing Windows
- --------------------
- Normally, you'd just call CloseWindow to close the current window with focus.
- However, you may need to close some other window, or you may not be sure
- that the window you want to close has focus (remember, the used could click on
- any window and give it focus). Assume that you know the number of the window
- to be closed (the window number is returned by the function used to open it).
- If the window's number is saved in variable WN, then the following technique
- can be used to make sure that the window has focus before calling CloseWindow:
-
- IF IsWinOpen(WN,han) THEN ' if window is open, get its handle --> han
- zz=CurWinPtr ' save handle of current window with focus
- CALL NewFocusWindow(han) ' make the target window active
- x=CloseWindow ' and close it
- CALL NewFocusWindow(zz) ' return focus to original window
- END IF
-
- If you wanted to close several windows, and you did not know which
- (if any) were open, you'd have to repeat the above code for each possible
- window number to make sure all windows were closed. In this case, you could
- just save the current window's handle once [ zz=CurWinPtr ] and restore it
- after all windows were closed [ CALL NewFocusWindow(zz) ].
-
- There's another "short-cut" you can use if you just want to close ALL open
- windows (if you want to close a sub-set of all possible windows, you'll have
- to use the above technique with the numbers of each window to be closed).
- To easily close ALL open windows:
-
- FOR i = LastWinStack TO 1 STEP -1
- CALL NewFocusWindow(WinStack(i))
- x=CloseWindow
- NEXT
-
- The WinStack array contains the handles of all open windows, in the order
- in which they appear on the screen. LastWinStack is a global variable pointing
- to the current slot. Thus WinStack(1) contains the handle of the first window
- opened, and WinStack(LastWinStack) contains the handle of the window with
- focus. The above code just closes each window, from the most current to the
- least current.
-
-
- 6.21 Modifying Static Text Created With ShowWinText
- ---------------------------------------------------
- The ShowWinText function is used to place static text in a window. Parameters
- include relative row/column (within the window) where the text is to be
- placed, text color, and text contents. The static text can be modified/changed
- by calling ShowWinText with the same row/column but new text. This is
- acceptable when only a few updates are needed. However, if the static text is
- modified based upon some user action (like a button click), then there is no
- way to determine the number of times the text will be modified. Each time
- ShowWinText is called, the text is saved in LangWin's data structures
- (ButtonsText and ButtonsData). These structures have a finite dimension
- (MaxButtons). Each call to ShowWinText requires a new entry in the data
- structures. Thus, repeated calls to modify text can exhaust all available
- slots. In this case, the result would be that subsequent calls to create
- objects (either new buttons, input fields, check boxes, or static text) will
- fail and the object will not appear. Closing a window will free all slots
- occupied by its objects. Increasing the value of MaxButtons will make more
- slots available. However, neither of these is a good solution. A better
- solution is to re-use the same slot whenever existing static text is modified.
- All that is needed is to first determine the slot number (handle) of the
- static text, then to update the contents of the existing slot and re-display
- it.
-
- Since ShowWinText returns an error code and not the handle of the slot used to
- store text, you'll need to find and save the handle number in order to later
- modify its contents. The best way to do this is to use ShowWinText to
- temporarily place some specific text into the data structures, then manually
- scan the data structure until you find the specific text. The following code
- will accomplish these tasks:
-
-
- x=ShowWinText(r,c,colr,"SOME UNIQUE TEXT")
- ' the above will place "SOME UNIQUE TEXT" at row=r, column=c in the
- ' current window using color=colr. the string "SOME UNIQUE TEXT"
- ' will also be placed into the next available slot in ButtonsText
- IF x < 0 THEN
- ' if error condition, process it
- END
- END IF
-
- thandle = -999 ' set a default value for the handle
-
- ' now search ButtonsText for the specific text string
- FOR i = 1 TO MaxButtons
- IF ButtonsText(i) = "SOME UNIQUE TEXT" THEN
- thandle=i ' save handle number
- EXIT FOR ' quit serach
- END IF
- NEXT
-
- ' just in case the "impossible" happens
- IF thandle = -999 THEN
- ' process the error
- END
- END IF
-
- At this point, the variable: thandle points to the slot in LangWin's data
- structures that contains the temporary static text. The screen itself displays
- the temporary text at the designated row/column. Now, you must replace the
- temporary text with the initial value of the static text (using the same slot
- in the data structures) and display this text on the screen. The following
- code will accomplish these tasks:
-
- a$="initial contents of static text field"
- ButtonsText(thandle)=a$ ' update text in data structure
- CALL ReShowInputField(thandle) ' redisplay text on screen
- ButtonsData(thandle,4) = LEN(a$) ' update length of text area
-
- You might ask: why is the length of the text in LangWin's data structure
- updated AFTER the text is displayed? Glad you asked that question!
- ReShowInputField (which is really meant to redisplay the contents of an input
- field after it has been modified, but will also redisplay static text) first
- clears the entire contents of the area, then displays the new text. The length
- of the area to clear is obtained from the data structure. So, if the length of
- the new text was smaller than the previous text, and the new (smaller) length
- was first placed into the data structure, then only the smaller area would
- cleared prior to displaying the new text. Any characters from the previous
- (longer) text beyond the end of the new (smaller) text would remain on the
- screen. By calling ReShowInputField before the length is updated, the length
- of the area cleared will correspond to the the current text.
-
- Later in your program, when you determine that new static text must be
- placed into the window at the same position as the old text, the following
- code can be used:
-
- a$="new contents of static text field"
- ButtonsText(thandle)=a$ ' update text in data structure
- CALL ReShowInputField(thandle) ' redisplay text on screen
- ButtonsData(thandle,4) = LEN(a$) ' update length of text area
-
- These techniques can be used with any number of static text entries. You'll
- just need to save each handle in a unique variable name.
-
-
- 6.22 "Time Out" Feature of WinEvent for Interrupt Buttons
- ---------------------------------------------------------
- Normally, when you call WinEvent, your program only regains control when
- WinEvent detects an event. If no event occurs, WinEvent retains control, and
- your program cannot do any work. In most cases, this is ok. You don't need to
- do any work until an event occurs in a window. Then, your program will get
- control from WinEvent, determine the window and the event that occurred, take
- the appropriate actions, and return control to WinEvent to determine the next
- event in the window.
-
- However, if the action you take involves starting a long running task, then
- WinEvent will not be given control until this long running task completes, and
- there is no way to interrupt the long running task (because WinEvent will not
- have control, events in the window, like button clicks, will not be detected).
-
- This is where the "time out" feature of WinEvent comes in. With the "time out"
- feature enabled, if no event occurs within 0.5 seconds, WinEvent will return
- control to your program. WinEvent is called with one parameter (the action
- code). Prior to returning control to your program, WinEvent sets this action
- code based upon the event that was detected (1 = close, 2 = text click, 3 =
- button click). To enable the time out feature, call WinEvent with the action
- code variable set to -999 (any other value for the the action code will have no
- effect on WinEvent). If an event occurs, the action code variable is set
- appropriately. If no event occurs, and the action code was -999, control will
- be returned after 0.5 seconds (and the action code will remain set to -999).
- WARNING: do not call WinEvent(-999) to enable the time out. You must call
- WinEvent with a variable since this variable will be set if an event occurs.
- To enable the time out feature, call WinEvent as follows:
-
- ActCode=-999
- wn=WinEvent(ActCode)
-
- In order to interrupt a long running task, place the task in a loop with a
- call to WinEvent (with time out feature enabled). Each time through the loop,
- some portion of the long running task is completed (like reading one record
- from a large file, scanning one directory, whatever portion of the overall
- task that is appropriate). After completing a portion of the long running
- task, call WinEvent with the time out feature. After the call to WinEvent,
- determine what (if any) event occurred. If the event was a click on your
- interrupt button, exit the loop that processed the long running task. If no
- event occurred (i.e., WinEvent timed out after 0.5 seconds), then loop and
- perform more work on the long running task.
-
- Here's some pseudo code. Assume this code gets control when a "start" button
- is clicked to initiate the long running task. Also assume that prior to this
- point, a MODAL window was opened with start and interrupt buttons for the
- long running task. Finally, assume that the long running task is to read all
- records from a file.
-
- CASE StartButton
- deactivate "start" button (don't need it now)
- activate "interrupt" button (assume it was previously inactive)
- open a file (assume the long running task is to read a file)
-
- ' process long running task
- DO
- read a record (assume the long running task is to read a file)
- update screen to show some kind of progress
- if EOF then EXIT DO ' see if task is done
- act=-999 ' to activate time out feature
- wn=WinEvent(act) ' get an action or time out
- LOOP UNTIL interrupt button action detected
-
- ' long running task completed or interrupted
-
- deactivate "interrupt" button (not needed now)
- activate the "start" button (if it's needed)
- close file
- process the data
- close the current window (if it's no longer needed)
-
-
- Note that you should not attempt to do too much work in your loop before
- periodically giving control to WinEvent (to see if the interrupt button was
- clicked). WinEvent is not aware of mouse clicks made before it is called. This
- is because it must hide and re-show the mouse cursor during initialization
- (which causes the mouse driver to zero the mouse press counter). Thus, if you
- do a lot of processing before calling WinEvent, your user could have been
- attempting to click the interrupt button for awhile with no response. You'll
- have to experiment, and tune your code so that it gets back to WinEvent
- quickly. If you notice that the interrupt button needs to be clicked very
- frequently before it is recognized, then the cause is probably due to an
- excessive amount of time consumed by your task in between iterations of the
- loop where WinEvent is called.
-
- I strongly recommend that the window you open, with buttons to start and
- interrupt the long running task, be MODAL (that is, only events in that window
- will be recognized by WinEvent, events in other windows will be ignored). The
- "in-line" call to WinEvent (in the loop that processes your long running task)
- will restrict the events recognized by your program to just those that you
- explicitly test for after returning from WinEvent (the code you have elsewhere
- in your program to handle other buttons in other windows will not be given
- control). If the window with the start and interrupt buttons is not modal,
- then your user will be able to mouse to other windows, make them active, and
- click their buttons. This will have visual effects (active windows will move
- to the top, clicked buttons will move); however, unless explicit code to
- handle these events (in other windows) is included in your loop with the long
- running task, the clicks on other buttons will be ignored. This will confuse
- your user. By making the window with the start and interrupt buttons modal,
- your user will not be able to mouse to another window to make it active, and
- clicking objects on other windows will show no visual effect.
-
- See SAMPLE05.BAS for an example of the technique for using WinEvent's time out
- feature to implement an interrupt button.
-
-
- 6.23 Dynamically Adding Entries to a Visible List of Scrollable Text
- --------------------------------------------------------------------
- The OpenScrollWindow function is used to create a window with scrollable text.
- After the window is opened, you may want to add entries to the end of this list
- in real-time and display these new entries in the visible window.
-
- The GrowScrollText function will accomplish this task. This function takes one
- parameter: a text string. The contents of this text string is appended to the
- end of the scrollable text array associated with the current window, and the
- visible text in the window is re-displayed. The nature of that re-display
- depends upon whether or not the existing scrollable text fills the text area
- defined for the window.
-
- If the list of scrollable text in the current window does not fill the text
- area defined when the window was opened, then the text string passed to
- GrowScrollText will be be displayed at the bottom of the visible list. If the
- list of scrollable text fills the text area, then text is scrolled up and the
- new string passed to GrowScrollText is displayed at the bottom of the text
- area. Note that the list of scrollable text could be null, in which case the
- first call to GrowScrollText will create the first line of scrollable text in
- the current window (but a scrollable text window must have been opened,
- otherwise GrowScrollText will not work properly).
-
- GrowScrollText will operate on the current window with focus. If that window
- is not a scrollable text window, no action is taken and an error code (-1) is
- returned (i.e., even if the scrollable text array is null, the window MUST
- have been opened with OpenScrollText and NOT with BlankWin). If the scrollable
- text array for the current window is already full (i.e., it has MaxTextLines
- lines of text already defined), no action is taken and an error code (-2) is
- returned.
-
- GrowScrollText can be used to give the user visual feedback as a long running
- task progresses. Suppose you have a routine that searches for specific records
- in a data base, and you want to display these in a scrollable list for your
- user to browse. GrowScrollText can be used to show the records being
- dynamically added to the list as they are found. The following pseudo code
- will implement this example:
-
-
- Dim Text(1 to 1) AS STRING ' scrollable text, init as null
- ' The above array need only have 1 entry (initiallized to null).
- ' It's only used by OpenScrollWindow to initialize LangWin's data structure
- ' SaveText. Thereafter, the scrollable text is actually "grown" in SaveText.
- .
- .
- w1=OpenScrollWindow( ..... ,Text(), .....) 'scrollable window with null text
- ' put some buttons in the window
- .
- .
- DO WHILE AnyWinOpen ' process windows
- wn=WinEvent(action) ' wait for an action
- ' process actions in windows
- .
- .
- .
-
- ' assume that at this point you determine the need to search a
- ' database for records, and to display the results in a scrollable
- ' window (w1) where the user can browse the results.
-
- ' w1 is the number of the open window that will contain the
- ' scrollable text. at this point, the scrollable text array is null.
- x=IsWinOpen(w1,Han) ' get handle of w1 and save in Han
-
- ' search loop
- DO
- ' first, you must make sure window with text (w1) is current
- CALL NewFocusWindow(Han) ' give focus to text window
-
- rec$=GetNextMatch$(parms) ' get a record
- IF rec$="" THEN EXIT DO ' see if search is done
- ' note: GetNextMatch$ is NOT part of LangWin. it's
- ' a fictitious function used to illustrate a routine
- ' that you might call to get a record and return in text string.
- ' i assume a null value is returned when search is completed.
-
- rc=GrowScrollText(rec$) ' display rec in window
- SELECT CASE rc ' test above return code
- CASE -1 ' current window is not a scrollable text window
- ' process the error
- CASE -2 ' scrollable text array is full
- ' process the error. this might include allowing
- ' the user to browse the scrollable text and waiting
- ' for a CONTINUE button to be clicked. when this
- ' button is clicked, you could close the window,
- ' re-open it (to clear text), and continue the search.
- END SELECT
-
- ' using the time-out techniques from Section 6.22, you could
- ' also include a STOP button to interrupt the search.
- ' assume handle of STOP button is: Stop1
- aa=-999 ' set time out option
- x=WinEvent(aa) ' wait for 0.5 sec for an event
- ' loop until STOP button is clicked
- LOOP UNTIL (aa=3 AND WinParms(CurWinPtr,16)=Stop1)
-
- See SAMPLE05.BAS for actual code that used GrowScrollText.
-
-
-
- ------------------
- 7.0 MOUSE ROUTINES
- ------------------
- LangWin has several general mouse routines that can be used to control the
- mouse and get status. These are documented in WINHELP.BAS and include:
-
- MouseExists - test to see if mouse driver is installed
- InitMouse - initialize the mouse
-
- GetButtonPress - get position of mouse when a button was last clicked
- GetMousePos - get current position of mouse
- SetMousePos - set current position of mouse
-
- SetXLimit - set horizontal limits of mouse
- SetYLimit - set vertical limits of mouse
-
- ShowMouseCursor - make the mouse pointer visible
- HideMouseCursor - hide the mouse pointer
-
- SAMPLE03.BAS contains sample code using the above routines. Load WINHELP.BAS
- into QB and hit F2 to see details on each of these routines.
-
-
-
- -------------------------------
- 8.0 DIRECTORY AND FILE ROUTINES
- -------------------------------
- LangWin 2.0 has several functions that allow you to easily determine the
- current drive and directory, set the current drive and directory, and extract a
- list of files and sub-directories from the current directory. These are
- documented in WINHELP.BAS and include:
-
-
- ChangeDir - change the default directory on a specified drive
- ChangeDrive - change the default drive
- GetCurDir$ - get the default directory on a specified drive
- GetCurDrive$ - get the default drive letter
- GetFileNames - get either file names or sub-dir names from the
- current directory and save them in a string array
-
- SAMPLE04.BAS contains code that use these functions.
-
-
-
-
- ----------------------
- 9.0 COMING ATTRACTIONS
- ----------------------
- With LangWin 2.1, you now have a professional quality toolkit for creating a
- GUI in QuickBASIC. However, screen design with LangWin does leave a lot to be
- desired. You have to manually figure out all of the screen coordinates for your
- windows, and remember which parameters in what functions to use. LangWin
- now has the ability to load WINHELP.BAS, so at least you can review the
- parameter lists while you are developing. Still, it's a labor intensive process
- to design the screen.
-
- I am considering an graphical "front end" program that will allow you to design
- your screen interactively using the mouse. You'll click on the coordinates
- where windows are to be created, select colors, select objects from a menu and
- place them into the window. When you are satisfied, the "front end" will then
- generate the appropriate CALLs to LangWin routines. If you've read about or
- seen VBDOS, this should sound familiar. So, stay tuned for LangWin 3.0.
-
-
-
- --------------------------
- 10.0 CONTACTING THE AUTHOR
- --------------------------
- If you have any questions about LangWin, suggestions for improvement, or
- want to report a bug (ugh), send U.S. Mail to:
-
- Allen Lang
- 121 Windsor Commons
- Cranbury, NJ 08512
-
- If you have access to Prodigy, my ID is: KJNX76A
-
- Before reporting a bug, I'll need to know your version of LangWin.
- I've included a routine to extract the version number (GetVerNum$).
- Go into QuickBASIC's immediate window and enter: print GetVerNum$
-
- Before contacting me, I'd highly recommend that you: read ALL of the doc in
- this file, read the descriptions for all LangWin's routines (load WINHELP.BAS
- into QB and hit F2), and thoroughly examine the sample code included with this
- distribution. While that will require an investment of your time, you'll be
- able to fully utilize the power of LangWin, and you'll probably find the answer
- to most of your questions. Good luck and happy coding!!!
-
-
- ----------------end-of-author's-documentation---------------
-
- Software Library Information:
-
- This disk copy provided as a service of
-
- Public (software) Library
-
- We are not the authors of this program, nor are we associated
- with the author in any way other than as a distributor of the
- program in accordance with the author's terms of distribution.
-
- Please direct shareware payments and specific questions about
- this program to the author of the program, whose name appears
- elsewhere in this documentation. If you have trouble getting
- in touch with the author, we will do whatever we can to help
- you with your questions. All programs have been tested and do
- run. To report problems, please use the form that is in the
- file PROBLEM.DOC on many of our disks or in other written for-
- mat with screen printouts, if possible. PsL cannot debug pro-
- programs over the telephone, though we can answer questions.
-
- Disks in the PsL are updated monthly, so if you did not get
- this disk directly from the PsL, you should be aware that the
- files in this set may no longer be the current versions. Also,
- if you got this disk from another vendor and are having prob-
- lems, be aware that some files may have become corrupted or
- lost by that vendor. Get a current, working disk from PsL.
-
- For a copy of the latest monthly software library newsletter
- and a list of the 4,000+ disks in the library, call or write
-
- Public (software) Library
- P.O.Box 35705
- Houston, TX 77235-5705
-
- Orders only:
- 1-800-2424-PSL
- MC/Visa/AmEx/Discover
-
- Outside of U.S. or in Texas
- or for general information,
- Call 1-713-524-6394